Signatur
Beschreibung
Ein Interventionsbericht entsteht, wenn der Browser absichtlich nicht das tut, was der Code verlangt. Meist geht es dabei um Sicherheit, Leistung oder Nutzerfreundlichkeit. Typische Fälle sind ein preventDefault() in einem passiven touchstart-Listener, den der Browser ignoriert, ein per document.write() eingefügtes Cross-Origin-Skript, das bei langsamer Verbindung blockiert wird, oder ein navigator.vibrate() ohne vorherige Nutzeraktion.
Ein InterventionReport ist kein Objekt, das man selbst erzeugt. Man bekommt es über einen ReportingObserver, der mit types: ["intervention"] angelegt wurde. Alternativ schickt der Browser den Bericht als JSON per POST an einen Endpunkt, den der HTTP-Header Reporting-Endpoints festlegt (Name default).
Das Wörterbuch hat drei Eigenschaften:
type: immer der String"intervention".url: die URL des Dokuments, in dem der Eingriff passiert ist.body: einInterventionReportBodymit den Feldernid,message,sourceFile,lineNumberundcolumnNumber, außerdem der MethodetoJSON().
Die Berichte sind vor allem für Monitoring im Produktivbetrieb nützlich. So sieht man, an welchen Stellen echter Nutzer der Browser eingreift und der Code sich deshalb anders verhält als geplant.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | "intervention" | Art des Berichts. Bei Interventionsberichten immer "intervention". |
| $url Pflicht | string | URL des Dokuments, das den Bericht ausgelöst hat. | |
| $body Pflicht | InterventionReportBody | Details zum Eingriff: id (Kennung des Eingriffs, z. B. "NavigatorVibrate"), message (lesbare Beschreibung), sourceFile, lineNumber, columnNumber (Fundstelle, sonst null). |
Rückgabewert
ReportingObserver übergeben wird oder das takeRecords() zurückgibt.Beispiele
Interventionen mit ReportingObserver abfangen
const observer = new ReportingObserver((reports) => {
for (const report of reports) {
const { id, message, sourceFile, lineNumber, columnNumber } = report.body;
console.log(`[${report.type}] ${id}: ${message}`);
console.log(`Fundstelle: ${sourceFile}:${lineNumber}:${columnNumber}`);
}
}, { types: ['intervention'], buffered: true });
observer.observe();
// Löst ohne vorherige Nutzeraktion eine Intervention aus
navigator.vibrate?.(200);
Berichte an ein eigenes Monitoring senden
const sendReports = (reports) => {
const payload = reports.map((r) => ({
type: r.type,
url: r.url,
body: r.body.toJSON()
}));
navigator.sendBeacon('/api/reports', JSON.stringify(payload));
};
const observer = new ReportingObserver(sendReports, { types: ['intervention'] });
observer.observe();
// Beim Verlassen der Seite noch nicht zugestellte Berichte mitnehmen
addEventListener('pagehide', () => {
const pending = observer.takeRecords();
if (pending.length) sendReports(pending);
});
Serverseitiger Endpunkt (Node.js/Express) über Reporting-Endpoints
import express from 'express';
const app = express();
// Browser schickt Interventionsberichte an den Endpunkt "default"
app.use((req, res, next) => {
res.set('Reporting-Endpoints', 'default="https://example.com/reports"');
next();
});
app.post('/reports', express.json({ type: 'application/reports+json' }), (req, res) => {
for (const report of req.body) {
if (report.type === 'intervention') {
console.log('Intervention:', report.body.id, '-', report.body.message);
}
}
res.sendStatus(204);
});
app.listen(3000);
// Wichtig · Fallstricke
- Kompatibilität: Interventionsberichte liefern nur Chromium-basierte Browser. Firefox und Safari unterstützen
ReportingObserverteilweise, erzeugen aber keine Berichte vom Typintervention. Vor der Nutzung mit'ReportingObserver' in windowprüfen. - Die Werte von
body.idund der Text vonmessagesind nicht standardisiert und können sich zwischen Browserversionen ändern. Man sollte sich also nicht fest auf bestimmte Strings verlassen. sourceFile,lineNumberundcolumnNumberkönnennullsein, etwa bei Eingriffen ohne eindeutige Codestelle.- Ohne
buffered: truekommen nur Berichte an, die nachobserve()entstehen. Frühere Eingriffe beim Laden der Seite gehen sonst verloren. - Berichte werden gebündelt und verzögert zugestellt, nicht sofort beim Eingriff. Vor dem Verlassen der Seite deshalb
takeRecords()aufrufen. - Datenschutz: Die gemeldete
urlkann Abfrageparameter mit personenbezogenen Daten enthalten. Vor dem Speichern bereinigen. - Der ältere Header
Report-Toist veraltet. StattdessenReporting-Endpointsverwenden.