Ich kenne was, was Du nicht kennst: stacktrace.js

Tales from the Web side  –  0 Kommentare

Die Bibliothek stacktrace.js ermöglicht den browserunabhängigen Zugriff auf Stacktrace-Informationen und bezieht Lokalisierungsinformationen aus Source Maps mit ein, um von der minifizierten Version Rückschlüsse auf Zeilen- und Spaltennummer im Originalcode ziehen zu können.

"Ich kenne was, was Du nicht kennst"

... ist eine gemeinsame Serie von Golo Roden und Philip Ackermann, in der die beiden regelmäßig Module für JavaScript und Node.js vorstellen.

Der Zugriff auf Stacktrace-Informationen ist unter JavaScript leider nicht standardisiert, das Verhalten von Browser zu Browser daher verschieden. Zwar lässt sich über die Eigenschaft stack von Error-Objekten auf entsprechende Informationen zugreifen, allerdings hat das auch Einschränkungen: Beispielsweise sind die darin enthaltenen Informationen zu Zeilen- und Spaltennummer wenig hilfreich, sobald man es mit der minifizierten Version einer Bibliothek zu tun hat.

Einführung

Genau an diesen Punkten setzt die Bibliothek stacktrace.js an: Sie ermöglicht zum einen den browserunabhängigen Zugriff auf Stacktrace-Informationen, zum anderen bezieht sie Lokalisierungsinformationen aus Source Maps mit ein, um von der minifizierten Version Rückschlüsse auf Zeilen- und Spaltennummer im Originalcode ziehen zu können. Sie lässt sich sowohl im Browser als auch auch unter Node.js verwenden.

Intern verwendet stacktrace.js mehrere Bibliotheken: error-stack-parser für das Parsen von Stacktrace-Informationen, stacktrace-gps, um Lokalisierungsinformationen aus Source Maps zu extrahieren, stack-generator, um den Stacktrace ausgehend von arguments.callee zu generieren und stackframe für das zugrunde liegende Objektmodel. Alle Bibliotheken lassen sich dabei auch unabhängig von stacktrace.js verwenden.

Installation und Verwendung

Um stacktrace.js unter Node.js zu verwenden, muss es zunächst wie gewohnt über npm mit dem Befehl npm install stacktrace-js installiert werden. Anschließend lässt es sich wie folgt einbinden:

'use strict';
const StackTrace = require('stacktrace-js');

Über die Methode fromError() lässt sich dann ausgehend von einem Error-Objekt entsprechende Stacktrace-Informationen generieren:

const objectA = {
methodA() {
throw new Error();
}
}
const objectB = {
methodB() {
try {
objectA.methodA();
} catch(error) {
StackTrace
.fromError(error) // Stacktrace auf Basis von Fehlerobjekt
.then(handleStackTrace) // Ausgabe Stacktrace-Informationen
.catch(handleError) // Fehlerbehandlung
}
}
}
objectB.methodB();

Im entsprechenden Callback hat man anschließend Zugriff auf ein Array einzelner "Stack-Frames", die unter anderem jeweils den Namen der aktuellen Funktion, den Dateinamen, die Zeilen- und die Spaltennummer sowie die Originalmeldung aus dem Stacktrace enthalten:

const handleStackTrace = function(stackFrames) {
console.log('*************** Start stacktrace ***************');
stackFrames.forEach((stackFrame, index) => {
console.log(index);
console.log('\tFunktion: ', stackFrame.functionName);
console.log('\tDatei: ', stackFrame.fileName);
console.log('\tZeile: ', stackFrame.lineNumber);
console.log('\tSpalte: ', stackFrame.columnNumber);
console.log('\tOriginal: ', stackFrame.source);
});
console.log('************************************************');
};
const handleError = function(error) {
console.log(error.message);
};

Möchte man an einer Stelle auf Stacktrace-Informationen zugreifen, an der kein Error-Objekt zur Verfügung steht, lässt sich alternativ die Methode get() verwenden:

const objectB = {
methodB() {
StackTrace
.get()
.then(callback)
.catch(errorCallback)
}
}
objectB.methodB();

Möchte man aus irgendeinem Grund keine Promises verwenden bzw. das Ganze synchron durchführen, kann man zudem, wie in folgendem Listing zu sehen, auf die Methode getSync() zurückgreifen. Allerdings bezieht diese Methode keine Source Maps mit ein (der Zugriff auf ebendiese würde asynchron ablaufen).

const objectB = {
methodB() {
const stackTrace = StackTrace.getSync();
handleStackTrace(stackTrace);
}
}
objectB.methodB();

Bei Verwendung der generateArtificially() wird die Stacktrace-Information auf Basis von arguments.callee generiert, wodurch konkrete Funktionsargumente mit einbezogen werden können und an den jeweiligen stackFrame-Objekten über die Eigenschaft args bereitgestellt werden (allerdings funktioniert dies nicht im strikten Modus).

...
const handleStackTrace = function(stackFrames) {
...
stackFrames.forEach((stackFrame, index) => {
...
console.log('\tArgumente: ', stackFrame.args);
});
...
};
...
const objectB = {
methodB() {
StackTrace
.generateArtificially()
.then(handleStackTrace)
.catch(handleError)
}
}
objectB.methodB();

Alternativen und Fazit

Die Bibliothek stacktrace.js vereinheitlicht den Zugriff auf Stacktrace-Informationen und lässt sich sowohl im Browser als auch unter Node.js verwenden. Reicht einem eine Lösung, die nur unter Node.js bzw. unter V8 funktioniert, kann man auch auf das Modul stack-trace zurückgreifen. Sicherlich gibt es aber noch eine Fülle an weiteren Bibliotheken. Wie sieht es bei Ihnen aus? Welche Bibliothek, welches Node.js-Modul verwenden Sie? Oder haben Sie sich gar Ihre eigene Lösung geschrieben? Ich freue mich auf Ihr Feedback.