¿Cómo obtener el número de línea de la función de llamada de JavaScript? ¿Cómo obtener la URL de origen de la persona que llama en JavaScript?

109

Estoy usando lo siguiente para obtener el nombre de la función de llamada de JavaScript:

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

¿Hay alguna forma de descubrir el número de línea desde el que se llamó al método?

Además, ¿hay alguna manera de obtener el nombre del archivo JavaScript desde el que se llamó al método? ¿O la URL de origen?

Tal
fuente
2
No creo que esto sea posible en IE, o de lo contrario tendríamos una manera de evitar los mensajes de error CRAPPY que no brindan ningún detalle. ¡Pero si es posible, me ENCANTARÍA saberlo también!
Zoidberg
Si. Aquí hay una función de navegador cruzado que hace uso de los métodos patentados de cada navegador: github.com/eriwen/javascript-stacktrace [enlace fijo]
scotts

Respuestas:

99

Esto funciona para mí en 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
fuente
Funciona en nodo también. Inserte esto dentro de su función log () personalizada (que agrega cualquier otra solución alternativa útil que necesite, por ejemplo, fija para el registro de matrices de Chrome) y aún números de línea desde donde llamó a log ().
mikemaccana
60
No es necesario lanzar el error; simplemente crearlo es suficiente:var caller_line = (new Error).stack.split("\n")[4]
ELLIOTTCABLE
2
Fusionó esta sugerencia con otra respuesta similar para obtener una respuesta "estandarizada" de FF / Webkit - consulte stackoverflow.com/a/14841411/1037948
drzaus
1
También funciona en PhantomJS, pero tienes que lanzarlo, o de lo contrario el atributo "pila" no se establece en el error.
Joshua Richardson
1
@ELLIOTTCABLE actualmente en algunos navegadores como iOS safari, ¡necesitas lanzar la excepción! Entonces por qué no hacerlo?
arctelix
26

La solución de kangax introduce un alcance try..catch innecesario. Si necesita acceder al número de línea de algo en JavaScript (siempre que esté usando Firefox u Opera), simplemente acceda (new Error).lineNumber.

Eli Grey
fuente
11
Hola, gracias por ese complemento. ¿Sabe si es posible obtener el número de línea de la llamada anterior? Digamos que el método A llama a B, y ahora en BI le gustaría saber en qué línea bajo A se realizó la llamada.
Tal
85
Esto está marcado, pero no responde a la pregunta, que es cómo obtener el número de línea de la función de llamada .
mikemaccana
3
Además, esto es extremadamente limitado. La mejor solución es lanzar un error y usar expresiones regulares en el error.stack que está disponible en todos los navegadores modernos. Puede extraer fácilmente esa ruta, archivo, línea y columna. No hay problema.
arctelix
13

Me sorprendió que la mayoría de estas respuestas asumieran que deseaba manejar un error en lugar de generar trazas de depuración útiles para casos normales.

Por ejemplo, me gusta usar una console.logenvoltura como esta:

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

Esto termina imprimiendo una salida como la siguiente en mi consola:

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

Consulte https://stackoverflow.com/a/27074218/ y también ¿ Un contenedor adecuado para console.log con el número de línea correcto?

Ryan
fuente
1
funciona para el navegador Firefox, pero no funciona para node.js.
Cremallera el
1
debajo del nodo tienes que registrar la pila [2]
monika mevenkamp
5

A menudo, esto se logra arrojando un error del contexto actual; luego analizando el objeto de error en busca de propiedades como lineNumbery fileName(que tienen algunos navegadores)

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

var err = getErrorObject();

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

No olvide que la callee.callerpropiedad está obsoleta (y nunca estuvo realmente en ECMA 3rd ed. En primer lugar).

Recuerde también que se especifica que la descompilación de funciones depende de la implementación y, por lo tanto, puede producir resultados bastante inesperados. Escribí sobre eso aquí y aquí .

Kangax
fuente
Gracias, parece un poco problemático agregar este código en la aplicación que necesito. (algún marco de seguimiento js) ¿Conoce algún otro método que no esté en desuso que pueda usar?
Tal
Debería poder inspeccionar el objeto de error sin depender de obsoleto callee.caller.
kangax
No es necesario lanzar el Error. Simplemente use (nuevo Error) .lineNumber para acceder al número de línea actual en un script.
Eli Gray
@Elijah Eso es lo que veo en FF3. WebKit, por otro lado, se completa linesolo cuando se produce un error.
Kangax
¿Cuál es el sustituto de callee.caller? ¿Si necesito obtener el nombre de la función?
Tal
4

Parece que llego un poco tarde :), pero la discusión es bastante interesante, así que ... aquí va ... Suponiendo que desea construir un controlador de errores, y está utilizando su propia clase de controlador de excepciones como:

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

Y estás envolviendo tu código así:

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

Lo más probable es que tenga el controlador de errores en un archivo .js separado.

Notará que en la consola de errores de Firefox o Chrome, el número de línea de código (y el nombre del archivo) que se muestra es la línea (archivo) que arroja la excepción 'Error' y no la excepción 'errorHandler' que realmente desea para realizar la depuración. fácil. Lanzar sus propias excepciones es genial, pero en proyectos grandes, localizarlas puede ser un gran problema, especialmente si tienen mensajes similares. Entonces, lo que puede hacer es pasar una referencia a un objeto Error vacío real a su controlador de errores, y esa referencia contendrá toda la información que desee (por ejemplo, en Firefox puede obtener el nombre del archivo y el número de línea, etc. ; en Chrome obtienes algo similar si lees la propiedad 'pila' de la instancia de Error). En pocas palabras, puedes hacer algo como esto:

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

Ahora puede obtener el archivo real y el número de línea que generó su excepción personalizada.

rusu bogdan
fuente
4

El número de línea es en realidad algo estático, por lo que si solo lo desea para el registro, podría preprocesarse con algo como gulp. He escrito un pequeño complemento de gulp que hace exactamente eso:

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

Esto adjuntará el nombre del archivo y la línea a todos los registros de console.log, por lo que console.log(something)se volverá console.log('filePath:fileNumber', something). La ventaja es que ahora puede concanear sus archivos, transpilarlos ... y aún obtendrá la línea

Gabriel Furstenheim
fuente
Esto parece una gran sugerencia para situaciones en las que se usa un transpilador (por ejemplo, cuando se usa TypeScript). ¡Gracias!
Andy King
4

Me doy cuenta de que esta es una pregunta antigua, pero ahora hay un método llamado console.trace("Message")que le mostrará el número de línea y la cadena de llamadas al método que llevaron al registro junto con el mensaje que le pasó. Más información sobre trucos de registro de JavaScript están disponibles aquí en freecodecamp y esta publicación de blog mediana

Japheth Ongeri - inkalimeva
fuente
3

Si desea saber el número de línea con fines de depuración, o solo durante el desarrollo (por una razón u otra), puede usar Firebug (una extensión de Firefox) y lanzar una excepción.

Editar :

Si realmente necesita hacer eso en producción por alguna razón, puede preprocesar sus archivos javascript para que cada función realice un seguimiento de la línea en la que se encuentra. Conozco algunos marcos que encuentran la cobertura del código que usan esto (como JSCoverage ).

Por ejemplo, digamos que su llamada original es:

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

Podría escribir un preprocesador para convertirlo en:

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

Luego y(), podría utilizar arguments.callee.caller.linepara conocer la línea desde la que se llamó, como por ejemplo:

function y() {
  alert(arguments.callee.caller.line);
}
Sinan Taifour
fuente
1
Gracias, me gustaría esto en producción por razones de soporte. Encontré un código que le permite ver la pila de llamadas de todo el flujo hasta el método, pero no tiene los números de línea donde se llamaron los métodos. ¿Supongo que hay una solución fácil para eso?
Tal
3

Así es como lo he hecho, lo he probado tanto en Firefox como en Chrome. Esto permite comprobar el nombre de archivo y el número de línea del lugar desde donde se llama a la función.

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
fuente
2

Esto es lo que escribí según la información que se encuentra en este foro:

Esto es parte de un MyDebugNamespace, Debug aparentemente está reservado y no funcionará como nombre del espacio de nombres.

    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 llamar:

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

Incluí dos funciones con un número variable de argumentos llamando a este código base en mi espacio de nombres para omitir opcionalmente el mensaje o error en las llamadas.

Esto funciona bien con Firefox, IE6 y Chrome reportan el nombre de archivo y el número de línea como indefinidos.

pasx
fuente
2

el siguiente código me funciona en Mozilla y Chrome.

Su función de registro que muestra el nombre del archivo y la línea de la persona que llama.

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
fuente
Parece que falta algo. Recibo:SyntaxError: function statement requires a name,log: function (arg) {
spiderplant0
Me gusta esta idea, pero los números de línea me resultan incorrectos.
Ryan
2

Mi contribución a los errores personalizados en JavaScript:

  1. Primero, estoy de acuerdo con este tipo de @BT en Heredar del objeto Error: ¿dónde está la propiedad del mensaje? , tenemos que construirlo correctamente (en realidad, debes usar una biblioteca de objetos js, mi 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. luego, deberíamos definir una función de manejo de errores global (opcional) o, terminarán en el motor:

    window.onerror = printError; 
    function printError(msg, url, line){
        document.getElementById('test').innerHTML = msg+'<br>at: '+url+'<br>line: '+line;
        return true;
    }
  3. finalmente, debemos lanzar nuestros errores personalizados con 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());

¡Solo, al pasar el tercer parámetro, ya que new Error()podemos ver la pila con los números de función y línea!

En 2, la función también puede manejar errores lanzados por el motor.

Por supuesto, la verdadera pregunta es si realmente lo necesitamos y cuándo; hay casos (99% en mi opinión) en los que un retorno agraciado de falsees suficiente y dejar solo algunos puntos críticos para ser mostrados con el lanzamiento de un error.

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

centurian
fuente
2
console.log(new Error);

Te mostrará la pista completa.

RatajS
fuente
1

Para determinar en qué línea está algo, debe buscar en todo el código el código que ocupa la línea particular de interés y contar los caracteres "\ n" desde la parte superior hasta la de interés y agregar 1.

De hecho, estoy haciendo esto mismo en una aplicación que estoy escribiendo. Es un validador de mejores prácticas para HTML y aún está en desarrollo, pero el proceso de salida de errores que le interesaría está completo.

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


fuente
la línea particular de interés puede ser muchas ... Si tengo el mismo método llamado varias veces desde otro método, ¿cómo puedo saber (en ese otro método) de dónde llegó la llamada?
Tal
No podrá analizar la interpretación de JavaScript en el momento de la ejecución desde fuera del intérprete. Podría escribir un programa para rastrear la ruta de ejecución en su programa, pero sería ese programa el que se está ejecutando y no el código que desea analizar. Esta suele ser una tarea complicada que se realiza manualmente con la ayuda de herramientas. Si realmente desea ver lo que está sucediendo en su código a medida que se ejecuta, haga que escriba metadatos en la pantalla que le indiquen qué otras partes están ejecutando las decisiones.
-2

Las respuestas son sencillas. No y No (No).

En el momento en que JavaScript está ejecutando el concepto de archivos de origen / URL de los que se ha ido el archivo.

Tampoco hay forma de determinar un número de línea porque nuevamente, en el momento de la ejecución, la noción de "líneas" de código ya no es significativa en Javascript.

Las implementaciones específicas pueden proporcionar enlaces de API para permitir el acceso de código privilegiado a dichos detalles con el fin de depurar, pero estas API no están expuestas al código JavaScript estándar ordinario.

AnthonyWJones
fuente
En firefox, las excepciones incluyen dicha información ... ¿sería posible al menos en firefox?
Zoidberg
Cuando inicia el depurador de MS Script y coloca un punto de interrupción, ve en la pila de llamadas de dónde viene exactamente. ¿Esto se debe a ganchos especializados?
Tal
"de nuevo, en el momento de la ejecución, la noción de" líneas "de código ya no tiene sentido en Javascript". ¿Eh? JS ya muestra el número de línea cada vez que ejecuta console.log ()
mikemaccana
@AnthonyWJones Sí. Contradice claramente el algo absoluto 'No y No (No)'.
mikemaccana
@nailer: En 2009 en todos los principales navegadores, ¿de qué manera se contradice mi respuesta? Tenga en cuenta que la pregunta en cuestión es sobre el descubrimiento, al ejecutar javascript, del número de línea del destinatario
AnthonyWJones