estructura - ¿Cómo puedo obtener un seguimiento de pila de JavaScript cuando lanzo una excepción?
pilas en javascript (24)
Aquí hay una respuesta que le brinda el máximo rendimiento (IE 6+) y la máxima compatibilidad. Compatible con IE 6!
function stacktrace( log_result ) {
var trace_result;
// IE 6 through 9 compatibility
// this is NOT an all-around solution because
// the callee property of arguments is depredicated
/*@cc_on
// theese fancy conditinals make this code only run in IE
trace_result = (function st2(fTmp) {
// credit to Eugene for this part of the code
return !fTmp ? [] :
st2(fTmp.caller).concat([fTmp.toString().split(''('')[0].substring(9) + ''('' + fTmp.arguments.join('','') + '')'']);
})(arguments.callee.caller);
if (log_result) // the ancient way to log to the console
Debug.write( trace_result );
return trace_result;
@*/
console = console || Console; // just in case
if (!(console && console.trace) || !log_result){
// for better performance in IE 10
var STerror=new Error();
var unformated=(STerror.stack || STerror.stacktrace);
trace_result = "/u25BC console.trace" + unformated.substring(unformated.indexOf(''/n'',unformated.indexOf(''/n'')));
} else {
// IE 11+ and everyone else compatibility
trace_result = console.trace();
}
if (log_result)
console.log( trace_result );
return trace_result;
}
// test code
(function testfunc(){
document.write( "<pre>" + stacktrace( false ) + "</pre>" );
})();
Si yo mismo lanzo una excepción de JavaScript (por ejemplo, throw "AArrggg"
), ¿cómo puedo obtener el seguimiento de la pila (en Firebug o de otra manera)? Ahora mismo acabo de recibir el mensaje.
Editar : Como muchas personas a continuación han publicado, es posible obtener un seguimiento de pila para una excepción de JavaScript, pero quiero obtener un seguimiento de pila para mis excepciones. Por ejemplo:
function foo() {
bar(2);
}
function bar(n) {
if (n < 2)
throw "Oh no! ''n'' is too small!"
bar(n-1);
}
Cuando se llama a foo
, quiero obtener un seguimiento de pila que incluya las llamadas a foo
, bar
, bar
.
Con el navegador Chrome, puede usar el método console.trace
: https://developer.chrome.com/devtools/docs/console-api#consoletraceobject
En Firefox parece que no es necesario lanzar la excepción. Es suficiente hacer
e = new Error();
console.log(e.stack);
En Google Chrome (versión 19.0 y posteriores), simplemente lanzar una excepción funciona perfectamente. Por ejemplo:
/* file: code.js, line numbers shown */
188: function fa() {
189: console.log(''executing fa...'');
190: fb();
191: }
192:
193: function fb() {
194: console.log(''executing fb...'');
195: fc()
196: }
197:
198: function fc() {
199: console.log(''executing fc...'');
200: throw ''error in fc...''
201: }
202:
203: fa();
mostrará el seguimiento de la pila en la salida de la consola del navegador:
executing fa... code.js:189
executing fb... code.js:194
executing fc... cdoe.js:199
/* this is your stack trace */
Uncaught error in fc... code.js:200
fc code.js:200
fb code.js:195
fa code.js:190
(anonymous function) code.js:203
Espero que esto ayude.
Es más fácil obtener un seguimiento de pila en Firefox que en IE, pero básicamente esto es lo que quieres hacer:
Envuelva el fragmento de código "problemático" en un bloque try / catch:
try {
// some code that doesn''t work
var t = null;
var n = t.not_a_value;
}
catch(e) {
}
Si examinas el contenido del objeto "error", contiene los siguientes campos:
e.fileName: el archivo / página de origen de donde provino el problema e.lineNumber: el número de línea en el archivo / página donde surgió el problema e.message: un mensaje simple que describe qué tipo de error tuvo lugar e.name: el tipo del error que tuvo lugar, en el ejemplo anterior debería ser ''TypeError'' e.stack: contiene el seguimiento de pila que causó la excepción
Espero que esto te ayude.
Es un poco tarde para la fiesta, pero, aquí hay otra solución, que detecta automáticamente si hay argumentos disponibles, y utiliza un nuevo Error (). Se apila si no. Probado en cromo, safari y firefox.
2 variantes: stackFN (n) le da el nombre de la función n fuera del llamante inmediato, y stackArray () le da una matriz, stackArray () [0] es el llamador inmediato.
Pruébelo en http://jsfiddle.net/qcP9y/6/
// returns the name of the function at caller-N
// stackFN() = the immediate caller to stackFN
// stackFN(0) = the immediate caller to stackFN
// stackFN(1) = the caller to stackFN''s caller
// stackFN(2) = and so on
// eg console.log(stackFN(),JSON.stringify(arguments),"called by",stackFN(1),"returns",retval);
function stackFN(n) {
var r = n ? n : 0, f = arguments.callee,avail=typeof f === "function",
s2,s = avail ? false : new Error().stack;
if (s) {
var tl=function(x) { s = s.substr(s.indexOf(x) + x.length);},
tr = function (x) {s = s.substr(0, s.indexOf(x) - x.length);};
while (r-- >= 0) {
tl(")");
}
tl(" at ");
tr("(");
return s;
} else {
if (!avail) return null;
s = "f = arguments.callee"
while (r>=0) {
s+=".caller";
r--;
}
eval(s);
return f.toString().split("(")[0].trim().split(" ")[1];
}
}
// same as stackFN() but returns an array so you can work iterate or whatever.
function stackArray() {
var res=[],f = arguments.callee,avail=typeof f === "function",
s2,s = avail ? false : new Error().stack;
if (s) {
var tl=function(x) { s = s.substr(s.indexOf(x) + x.length);},
tr = function (x) {s = s.substr(0, s.indexOf(x) - x.length);};
while (s.indexOf(")")>=0) {
tl(")");
s2= ""+s;
tl(" at ");
tr("(");
res.push(s);
s=""+s2;
}
} else {
if (!avail) return null;
s = "f = arguments.callee.caller"
eval(s);
while (f) {
res.push(f.toString().split("(")[0].trim().split(" ")[1]);
s+=".caller";
eval(s);
}
}
return res;
}
function apple_makes_stuff() {
var retval = "iPhones";
var stk = stackArray();
console.log("function ",stk[0]+"() was called by",stk[1]+"()");
console.log(stk);
console.log(stackFN(),JSON.stringify(arguments),"called by",stackFN(1),"returns",retval);
return retval;
}
function apple_makes (){
return apple_makes_stuff("really nice stuff");
}
function apple () {
return apple_makes();
}
apple();
Este código de relleno múltiple que funciona en los navegadores modernos (2017) (IE11, Opera, Chrome, FireFox, Yandex):
printStackTrace: function () {
var err = new Error();
var stack = err.stack || /*old opera*/ err.stacktrace || ( /*IE11*/ console.trace ? console.trace() : "no stack info");
return stack;
}
Otras respuestas:
function stackTrace() {
var err = new Error();
return err.stack;
}
No funciona en IE 11!
Uso de argument.callee.caller : ¡no funciona en modo estricto en ningún navegador!
Esto le dará un seguimiento de la pila (como un conjunto de cadenas) para Chrome, Opera, Firefox e IE10 + modernos.
function getStackTrace () {
var stack;
try {
throw new Error('''');
}
catch (error) {
stack = error.stack || '''';
}
stack = stack.split(''/n'').map(function (line) { return line.trim(); });
return stack.splice(stack[0] == ''Error'' ? 2 : 1);
}
Uso:
console.log(getStackTrace().join(''/n''));
Excluye de la pila su propia llamada y el título "Error" que utilizan Chrome y Firefox (pero no IE).
No debería bloquearse en los navegadores antiguos, sino que solo devolvería una matriz vacía. Si necesita una solución más universal, consulte stacktrace.js . Su lista de navegadores compatibles es realmente impresionante, pero en mi opinión es muy grande para esa pequeña tarea a la que está destinada: 37 KB de texto minificado que incluye todas las dependencias.
No creo que haya algo incorporado que puedas usar, sin embargo, sí encontré muchos ejemplos de personas que lanzan sus propios.
Puede acceder a las propiedades de la stack
( stacktrace
en Opera) de una instancia de Error
incluso si la lanzó. El problema es que debe asegurarse de utilizar el throw new Error(string)
(no olvide la nueva throw string
lugar de la throw string
de throw string
.
Ejemplo:
try {
0++;
} catch (e) {
var myStackTrace = e.stack || e.stacktrace || "";
}
Puede utilizar esta biblioteca stacktrace.js . Es muy bueno
De la documentación
También puede pasar su propio error para obtener un seguimiento de pila no disponible en IE o Safari 5.
<script type="text/javascript" src="https://rawgithub.com/stacktracejs/stacktrace.js/master/stacktrace.js"></script>
<script type="text/javascript">
try {
// error producing code
} catch(e) {
var trace = printStackTrace({e: e});
alert(''Error!/n'' + ''Message: '' + e.message + ''/nStack trace:/n'' + trace.join(''/n''));
// do something else with error
}
</script>
Si tiene Firebug, hay una opción de interrupción en todos los errores en la pestaña de script. Una vez que el script haya alcanzado su punto de interrupción, puede mirar la ventana de pila de firebug:
Tenga en cuenta que chromium / chrome (otros navegadores que usan V8), y también Firefox tienen una interfaz conveniente para obtener un seguimiento de pila a través de una propiedad de pila en objetos de error .
try {
// Code throwing an exception
} catch(e) {
console.log(e.stack);
}
Se aplica tanto a las excepciones básicas como a las que usted mismo lanza. (Consideró que usa la clase Error, que de todos modos es una buena práctica).
Ver detalles en la documentación de V8.
Tuve que investigar una recursión sin fin en smartgwt con IE11, por lo que para investigar más a fondo, necesitaba un seguimiento de pila. El problema era que no podía usar la consola dev, porque la reproducción era más difícil de esa manera.
Usa lo siguiente en un método javascript:
try{ null.toString(); } catch(e) { alert(e.stack); }
Una actualización de la respuesta de Eugene: el objeto de error debe lanzarse para que IE (¿versiones específicas?) Rellene la propiedad de la stack
. Lo siguiente debería funcionar mejor que su ejemplo actual, y debería evitar regresar undefined
cuando undefined
en IE.
function stackTrace() {
try {
var err = new Error();
throw err;
} catch (err) {
return err.stack;
}
}
Nota 1: este tipo de cosas solo se deben hacer al depurar y deshabilitar cuando está activo, especialmente si se llama con frecuencia. Nota 2: Esto puede no funcionar en todos los navegadores, pero parece funcionar en FF e IE 11, lo que se adapta a mis necesidades.
Una buena (y simple) solución como se señala en los comentarios sobre la pregunta original es usar la propiedad de stack
de un objeto Error
manera:
function stackTrace() {
var err = new Error();
return err.stack;
}
Esto generará una salida como esta:
DBX.Utils.stackTrace@http://localhost:49573/assets/js/scripts.js:44
DBX.Console.Debug@http://localhost:49573/assets/js/scripts.js:9
.success@http://localhost:49573/:462
x.Callbacks/c@http://localhost:49573/assets/js/jquery-1.10.2.min.js:4
x.Callbacks/p.fireWith@http://localhost:49573/assets/js/jquery-1.10.2.min.js:4
k@http://localhost:49573/assets/js/jquery-1.10.2.min.js:6
.send/r@http://localhost:49573/assets/js/jquery-1.10.2.min.js:6
Dar el nombre de la función de llamada junto con la URL y el número de línea, su función de llamada, etc.
Tengo una solución muy elaborada y bonita que he ideado para un proyecto en el que estoy trabajando actualmente, y la he extraído y reelaborado un poco para generalizarla. Aquí está:
(function(context){
// Only global namespace.
var Console = {
//Settings
settings: {
debug: {
alwaysShowURL: false,
enabled: true,
showInfo: true
},
stackTrace: {
enabled: true,
collapsed: true,
ignoreDebugFuncs: true,
spacing: false
}
}
};
// String formatting prototype function.
if (!String.prototype.format) {
String.prototype.format = function () {
var s = this.toString(),
args = typeof arguments[0],
args = (("string" == args || "number" == args) ? arguments : arguments[0]);
if (!arguments.length)
return s;
for (arg in args)
s = s.replace(RegExp("//{" + arg + "//}", "gi"), args[arg]);
return s;
}
}
// String repeating prototype function.
if (!String.prototype.times) {
String.prototype.times = function () {
var s = this.toString(),
tempStr = "",
times = arguments[0];
if (!arguments.length)
return s;
for (var i = 0; i < times; i++)
tempStr += s;
return tempStr;
}
}
// Commonly used functions
Console.debug = function () {
if (Console.settings.debug.enabled) {
var args = ((typeof arguments !== ''undefined'') ? Array.prototype.slice.call(arguments, 0) : []),
sUA = navigator.userAgent,
currentBrowser = {
firefox: /firefox/gi.test(sUA),
webkit: /webkit/gi.test(sUA),
},
aLines = Console.stackTrace().split("/n"),
aCurrentLine,
iCurrIndex = ((currentBrowser.webkit) ? 3 : 2),
sCssBlack = "color:black;",
sCssFormat = "color:{0}; font-weight:bold;",
sLines = "";
if (currentBrowser.firefox)
aCurrentLine = aLines[iCurrIndex].replace(/(.*):/, "$1@").split("@");
else if (currentBrowser.webkit)
aCurrentLine = aLines[iCurrIndex].replace("at ", "").replace(")", "").replace(/( /()/gi, "@").replace(/(.*):(/d*):(/d*)/, "$1@$2@$3").split("@");
// Show info if the setting is true and there''s no extra trace (would be kind of pointless).
if (Console.settings.debug.showInfo && !Console.settings.stackTrace.enabled) {
var sFunc = aCurrentLine[0].trim(),
sURL = aCurrentLine[1].trim(),
sURL = ((!Console.settings.debug.alwaysShowURL && context.location.href == sURL) ? "this page" : sURL),
sLine = aCurrentLine[2].trim(),
sCol;
if (currentBrowser.webkit)
sCol = aCurrentLine[3].trim();
console.info("%cOn line %c{0}%c{1}%c{2}%c of %c{3}%c inside the %c{4}%c function:".format(sLine, ((currentBrowser.webkit) ? ", column " : ""), ((currentBrowser.webkit) ? sCol : ""), sURL, sFunc),
sCssBlack, sCssFormat.format("red"),
sCssBlack, sCssFormat.format("purple"),
sCssBlack, sCssFormat.format("green"),
sCssBlack, sCssFormat.format("blue"),
sCssBlack);
}
// If the setting permits, get rid of the two obvious debug functions (Console.debug and Console.stackTrace).
if (Console.settings.stackTrace.ignoreDebugFuncs) {
// In WebKit (Chrome at least), there''s an extra line at the top that says "Error" so adjust for this.
if (currentBrowser.webkit)
aLines.shift();
aLines.shift();
aLines.shift();
}
sLines = aLines.join(((Console.settings.stackTrace.spacing) ? "/n/n" : "/n")).trim();
trace = typeof trace !== ''undefined'' ? trace : true;
if (typeof console !== "undefined") {
for (var arg in args)
console.debug(args[arg]);
if (Console.settings.stackTrace.enabled) {
var sCss = "color:red; font-weight: bold;",
sTitle = "%c Stack Trace" + " ".times(70);
if (Console.settings.stackTrace.collapsed)
console.groupCollapsed(sTitle, sCss);
else
console.group(sTitle, sCss);
console.debug("%c" + sLines, "color: #666666; font-style: italic;");
console.groupEnd();
}
}
}
}
Console.stackTrace = function () {
var err = new Error();
return err.stack;
}
context.Console = Console;
})(window);
¡Compruébalo en GitHub (actualmente v1.2)! Puedes usarlo como Console.debug("Whatever");
y, según la configuración de la Console
, imprimirá la salida y un seguimiento de la pila (o simplemente información simple / nada extra). Aquí hay un ejemplo:
¡Asegúrate de jugar con la configuración en el objeto de la Console
! Puede agregar espaciado entre las líneas de la traza y desactivarla por completo. Aquí está con Console.trace
establecido en false
:
Incluso puede desactivar el primer bit de información que se muestra (establecer Console.settings.debug.showInfo
como false
) o deshabilitar la depuración por completo (configurar Console.settings.debug.enabled
como false
) para que nunca más tenga que comentar una declaración de depuración ! Solo déjalos y esto no hará nada.
Una forma de obtener el seguimiento de la pila real en Firebug es crear un error real, como llamar a una función no definida:
function foo(b){
if (typeof b !== ''string''){
// undefined Error type to get the call stack
throw new ChuckNorrisError("Chuck Norris catches you.");
}
}
function bar(a){
foo(a);
}
foo(123);
O use console.error()
seguido de una sentencia throw
ya que console.error()
muestra el seguimiento de la pila.
Una versión modificada de este fragmento puede ayudar un poco:
function stacktrace() {
function st2(f) {
return !f ? [] :
st2(f.caller).concat([f.toString().split(''('')[0].substring(9) + ''('' + f.arguments.join('','') + '')'']);
}
return st2(arguments.callee.caller);
}
EDIT 2 (2017):
En todos los navegadores modernos puedes simplemente llamar: console.trace();
(Referencia MDN)
EDIT 1 (2013):
Una solución mejor (y más simple) como se señala en los comentarios de la pregunta original es usar la propiedad de stack
de un objeto Error
manera:
function stackTrace() {
var err = new Error();
return err.stack;
}
Esto generará una salida como esta:
DBX.Utils.stackTrace@http://localhost:49573/assets/js/scripts.js:44
DBX.Console.Debug@http://localhost:49573/assets/js/scripts.js:9
.success@http://localhost:49573/:462
x.Callbacks/c@http://localhost:49573/assets/js/jquery-1.10.2.min.js:4
x.Callbacks/p.fireWith@http://localhost:49573/assets/js/jquery-1.10.2.min.js:4
k@http://localhost:49573/assets/js/jquery-1.10.2.min.js:6
.send/r@http://localhost:49573/assets/js/jquery-1.10.2.min.js:6
Dar el nombre de la función de llamada junto con la URL, su función de llamada, etc.
Usando console.error(e.stack)
Firefox solo muestra el seguimiento de pila en los registros, Chrome también muestra el mensaje. Esto puede ser una mala sorpresa si el mensaje contiene información vital. Siempre registre ambos.
Wow - ¡No veo a una sola persona en 6 años sugiriendo que primero verifiquemos si hay una stack
disponible antes de usarla! Lo peor que puedes hacer en un manejador de errores es lanzar un error al llamar a algo que no existe.
Como han dicho otros, aunque la mayoría de las stack
son seguras de usar ahora, no es compatible con IE9 o anterior.
Registro mis errores inesperados y un seguimiento de pila es bastante esencial. Para obtener el máximo soporte, primero compruebo si Error.prototype.stack
existe y es una función. Si es así, es seguro usar error.stack
.
window.onerror = function (message: string, filename?: string, line?: number,
col?: number, error?: Error)
{
// always wrap error handling in a try catch
try
{
// get the stack trace, and if not supported make our own the best we can
var msg = (typeof Error.prototype.stack == ''function'') ? error.stack :
"NO-STACK " + filename + '' '' + line + '':'' + col + '' + message;
// log errors here or whatever you''re planning on doing
alert(msg);
}
catch (err)
{
}
};
Edición: Parece que, dado que la stack
es una propiedad y no un método, puede llamarlo de manera segura incluso en navegadores más antiguos. Todavía estoy confundido porque estaba bastante seguro de que estaba comprobando Error.prototype
funcionó para mí anteriormente y ahora no, así que no estoy seguro de lo que está pasando.
función:
function print_call_stack(err) {
var stack = err.stack;
console.error(stack);
}
caso de uso:
try{
aaa.bbb;//error throw here
}
catch (err){
print_call_stack(err);
}
<script type="text/javascript"
src="https://rawgithub.com/stacktracejs/stacktrace.js/master/stacktrace.js"></script>
<script type="text/javascript">
try {
// error producing code
} catch(e) {
var trace = printStackTrace({e: e});
alert(''Error!/n'' + ''Message: '' + e.message + ''/nStack trace:/n'' + trace.join(''/n''));
// do something else with error
}
</script>
este script mostrará el error
function stacktrace(){
return (new Error()).stack.split(''/n'').reverse().slice(0,-2).reverse().join(''/n'');
}