Comment obtenir le numéro de ligne de fonction de l'appelant JavaScript? Comment obtenir l'URL de la source de l'appelant JavaScript?


109

J'utilise ce qui suit pour obtenir le nom de la fonction d'appelant JavaScript:

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

Existe-t-il un moyen de découvrir le numéro de ligne à partir duquel la méthode a été appelée?

Existe-t-il également un moyen d'obtenir le nom du fichier JavaScript à partir duquel la méthode a été appelée? Ou l'URL source?


2
Je ne pense pas que cela soit possible dans IE, sinon nous aurions un moyen de contourner les messages d'erreur CRAPPY qui ne fournissent aucun détail. Mais si c'est possible, j'adorerais aussi le savoir!
Zoidberg

Oui. Voici une fonction multi-navigateurs qui utilise les méthodes propriétaires de chaque navigateur: github.com/eriwen/javascript-stacktrace [lien fixe]
scotts

Réponses:


99

Cela fonctionne pour moi dans 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);

Fonctionne aussi dans le nœud. Insérez-le dans votre fonction log () personnalisée (qui ajoute toutes les autres solutions de contournement utiles dont vous avez besoin - par exemple, corrigé pour la journalisation des tableaux Chrome) et toujours les numéros de ligne de l'endroit où vous avez appelé log ().
mikemaccana

60
Pas besoin de lancer l'erreur; il suffit de le créer:var caller_line = (new Error).stack.split("\n")[4]
ELLIOTTCABLE

2
Fusion de cette suggestion avec une autre réponse similaire pour obtenir une réponse FF / Webkit «standardisée» - voir stackoverflow.com/a/14841411/1037948
drzaus

1
Fonctionne aussi dans PhantomJS, mais vous devez le lancer, sinon l'attribut "stack" n'est pas défini sur l'erreur.
Joshua Richardson

1
@ELLIOTTCABLE en fait dans certains navigateurs comme iOS Safari, vous devez lever l'exception! alors pourquoi ne pas le faire?
arctelix

26

La solution de kangax introduit une portée try..catch inutile. Si vous avez besoin d'accéder au numéro de ligne de quelque chose en JavaScript (tant que vous utilisez Firefox ou Opera), accédez simplement (new Error).lineNumber.


11
Salut, merci pour cet addon. savez-vous s'il est possible d'obtenir le numéro de ligne de l'appel précédent? Disons que la méthode A appelle B, et maintenant dans BI aimerait savoir dans quelle ligne sous A l'appel a été effectué?
Tal

85
Ceci est coché, mais ne répond pas à la question, à savoir comment obtenir le numéro de ligne de la fonction appelant .
mikemaccana

3
En outre, cela est extrêmement limité. La meilleure solution consiste à lancer une erreur et à utiliser regex sur error.stack qui est disponible dans tous les navigateurs modernes. Vous pouvez facilement extraire ce chemin, ce fichier, cette ligne et cette colonne. Aucun problème.
arctelix

13

J'ai été surpris que la plupart de ces réponses supposent que vous vouliez gérer une erreur plutôt que de simplement générer des traces de débogage utiles pour les cas normaux.

Par exemple, j'aime utiliser un console.logwrapper comme celui-ci:

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

Cela finit par imprimer une sortie telle que la suivante sur ma console:

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

Voir https://stackoverflow.com/a/27074218/ et également Un wrapper approprié pour console.log avec le numéro de ligne correct?


1
fonctionne pour le navigateur Firefox, mais ne fonctionne pas pour node.js.
fermeture éclair

1
sous le nœud, vous devez enregistrer la pile [2]
monika mevenkamp

5

Ceci est souvent réalisé en lançant une erreur du contexte actuel; puis analyser l'objet d'erreur pour des propriétés telles que lineNumberet fileName(que certains navigateurs ont)

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

var err = getErrorObject();

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

N'oubliez pas que la callee.callerpropriété est obsolète (et n'a jamais vraiment été dans ECMA 3e éd. En premier lieu).

Souvenez-vous également que la décompilation de fonction est spécifiée pour dépendre de l'implémentation et peut donc donner des résultats assez inattendus. J'en ai écrit ici et ici .


Merci, cela me semble un peu problématique d'ajouter ce code dans l'application dont j'ai besoin. (quelques frameworks de traçage js) Connaissez-vous une autre méthode qui n'est pas obsolète que je peux utiliser?
Tal

Vous devriez être en mesure d'inspecter l'objet d'erreur sans compter sur obsolète callee.caller.
kangax

Vous n'avez pas besoin de lancer l'erreur. Il vous suffit d'utiliser (new Error) .lineNumber pour accéder au numéro de ligne actuel dans un script.
Eli Gray

@Elijah C'est ce que je vois dans FF3. WebKit, en revanche, linene se remplit que lorsqu'une erreur est générée.
kangax

Quel est le substitut de callee.caller? Si j'ai besoin d'obtenir le nom de la fonction?
Tal

4

Il semble que je suis un peu en retard :), mais la discussion est assez intéressante alors ... la voici ... En supposant que vous vouliez créer un gestionnaire d'erreurs et que vous utilisiez votre propre classe de gestionnaire d'exceptions comme:

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

Et vous encapsulez votre code comme ceci:

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

Vous aurez très probablement le gestionnaire d'erreurs dans un fichier .js séparé.

Vous remarquerez que dans la console d'erreur de Firefox ou Chrome, le numéro de ligne de code (et le nom de fichier) affiché est la ligne (fichier) qui lève l'exception 'Error' et non l'exception 'errorHandler' que vous voulez vraiment pour effectuer le débogage facile. Lancer vos propres exceptions est génial, mais sur les grands projets, les localiser peut être un problème, surtout s'ils ont des messages similaires. Donc, ce que vous pouvez faire est de passer une référence à un objet Error vide réel à votre gestionnaire d'erreurs, et cette référence contiendra toutes les informations que vous voulez (par exemple, dans Firefox, vous pouvez obtenir le nom du fichier, le numéro de ligne, etc. ; dans Chrome, vous obtenez quelque chose de similaire si vous lisez la propriété 'stack' de l'instance Error). Pour faire court, vous pouvez faire quelque chose comme ceci:

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

Vous pouvez maintenant obtenir le fichier réel et le numéro de ligne qui vous ont généré une exception personnalisée.


4

Le numéro de ligne est en fait quelque chose de statique, donc si vous le voulez juste pour la journalisation, il peut être prétraité avec quelque chose comme gulp. J'ai écrit un petit plugin gulp qui fait exactement cela:

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

Cela attachera le nom de fichier et la ligne à tous les journaux de console.log, ainsi console.log(something)deviendra console.log('filePath:fileNumber', something). L'avantage est que maintenant vous pouvez concatrer vos fichiers, les transpiler ... et vous aurez toujours la ligne


Cela semble être une excellente suggestion pour les situations où un transpilateur est utilisé (par exemple, lors de l'utilisation de TypeScript). Je vous remercie!
Andy King

4

Je me rends compte que c'est une vieille question, mais il existe maintenant une méthode appelée console.trace("Message")qui vous montrera le numéro de ligne et la chaîne d'appels de méthode qui ont conduit au journal avec le message que vous lui transmettez. Plus d'informations sur les astuces de journalisation javascript sont disponibles ici sur freecodecamp et cet article de blog moyen


3

Si vous souhaitez connaître le numéro de ligne à des fins de débogage, ou uniquement pendant le développement (pour une raison ou une autre), vous pouvez utiliser Firebug (une extension Firefox) et lancer une exception.

Éditer :

Si vous avez vraiment besoin de le faire en production pour une raison quelconque, vous pouvez pré-traiter vos fichiers javascript afin que chaque fonction garde une trace de la ligne sur laquelle elle se trouve. Je connais certains frameworks qui trouvent que la couverture du code l'utilise (comme JSCoverage ).

Par exemple, disons que votre appel d'origine est:

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

Vous pouvez écrire un préprocesseur pour en faire:

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

Ensuite y(), vous pouvez utiliser arguments.callee.caller.linepour connaître la ligne à partir de laquelle il a été appelé, comme:

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

1
Merci, j'aimerais cela en production pour des raisons de support. J'ai trouvé du code qui vous permet de voir la pile d'appels de tout le flux jusqu'à la méthode, mais il n'a pas les numéros de ligne où les méthodes ont été appelées. Je suppose qu'une solution facile pour cela?
Tal

3

C'est comme ça que je l'ai fait, je l'ai testé dans Firefox et Chrome. Cela permet de vérifier le nom de fichier et le numéro de ligne de l'endroit d'où la fonction est appelée.

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

Voici ce que j'ai écrit sur la base des informations trouvées sur ce forum:

Cela fait partie d'un MyDebugNamespace, Debug est apparemment réservé et ne fonctionnera pas comme nom d'espace de noms.

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

...

Comment appeler:

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

J'ai inclus deux fonctions avec un nombre variable d'arguments appelant ce code de base dans mon espace de noms afin d'omettre éventuellement un message ou une erreur dans les appels.

Cela fonctionne bien avec Firefox, IE6 et Chrome signalent le fileName et le lineNumber comme non définis.


2

le code suivant fonctionne pour moi dans Mozilla et Chrome.

Sa fonction de journal qui montre le nom du fichier et la ligne de l'appelant.

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

Il manque quelque chose. Je reçois:SyntaxError: function statement requires a name,log: function (arg) {
spiderplant0

J'aime cette idée, mais les numéros de ligne sont incorrects pour moi.
Ryan

2

Ma contribution aux erreurs personnalisées en JavaScript:

  1. Tout d'abord, je suis d'accord avec ce gars @BT à Hériter de l'objet Error - où est la propriété du message? , nous devons le construire correctement (en fait, vous devez utiliser une bibliothèque d'objets js, ma préférée: 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. ensuite, nous devrions définir une fonction de gestion d'erreur globale (facultative) ou, ils finiront vers le moteur:

    window.onerror = printError; 
    function printError(msg, url, line){
        document.getElementById('test').innerHTML = msg+'<br>at: '+url+'<br>line: '+line;
        return true;
    }
  3. enfin, nous devons jeter soigneusement nos erreurs personnalisées:

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

Uniquement, lors du passage du troisième paramètre comme new Error() nous pouvons voir la pile avec les numéros de fonction et de ligne!

En 2, la fonction peut également gérer les erreurs générées par le moteur.

Bien sûr, la vraie question est de savoir si nous en avons vraiment besoin et quand; il y a des cas (99% à mon avis) où un retour gracieux de falseest suffisant et ne laisse que quelques points critiques à afficher avec la levée d'une erreur.

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


2
console.log(new Error);

Il vous montrera la piste entière.


1

Pour déterminer sur quelle ligne quelque chose se trouve, vous devez rechercher dans tout le code le code qui occupe la ligne d'intérêt particulière et compter les caractères "\ n" du haut vers celui qui vous intéresse et ajouter 1.

Je fais en fait cette chose même dans une application que j'écris. Il s'agit d'un validateur de bonnes pratiques pour HTML et il est encore en cours de développement, mais le processus de sortie d'erreur qui vous intéresserait est terminé.

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


la ligne d'intérêt particulière peut être plusieurs ... Si j'ai la même méthode appelée plusieurs fois à partir d'une autre méthode, comment puis-je savoir (dans cette autre méthode) d'où vient l'appel?
Tal

Vous ne pourrez pas analyser l'interprétation JavaScript au moment de l'exécution depuis l'extérieur de l'interpréteur. Vous pouvez écrire un programme pour tracer le chemin d'exécution dans votre programme, mais ce serait ce programme qui s'exécute et non le code que vous souhaitez analyser. Il s'agit généralement d'une tâche compliquée qui est effectuée manuellement à l'aide d'outils. Si vous voulez vraiment voir ce qui se passe dans votre code pendant son exécution, demandez-lui d'écrire des méta-données sur l'écran qui indiquent que vous voulez que les décisions exécutent quelles autres parties.

-2

Les réponses sont simples. Non et non (non).

Au moment où javascript exécute le concept de fichiers / URL sources dont le venu a disparu.

Il n'y a également aucun moyen de déterminer un numéro de ligne car encore une fois au moment de l'exécution, la notion de code "lignes" n'a plus de sens en Javascript.

Des implémentations spécifiques peuvent fournir des hooks d'API pour permettre un accès de code privilégié à ces détails à des fins de débogage, mais ces API ne sont pas exposées au code Javascript standard ordinaire.


Dans Firefox, les exceptions incluent de telles informations ... serait-ce possible au moins dans Firefox?
Zoidberg

Lorsque vous lancez le débogueur MS Script et mettez un point d'arrêt, vous voyez dans la pile d'appels d'où vous venez exactement. C'est à cause des crochets spécialisés?
Tal

"encore une fois au moment de l'exécution, la notion de" lignes "de code n'a plus de sens en Javascript." Hein? JS affiche déjà le numéro de ligne à chaque fois que vous exécutez console.log ()
mikemaccana

@AnthonyWJones Oui. Cela contredit parfaitement le «Non et Non (Non)» quelque peu absolu.
mikemaccana

@nailer: En 2009 sur tous les principaux navigateurs, en quoi ma réponse contredit-elle. Gardez à l'esprit que la question à portée de main concerne la découverte, en exécutant javascript, du numéro de ligne de l'appelé?
AnthonyWJones
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.