Audit Log UI in React: Filter, Diffs und CSV-Export
Früher oder später fragt ein Kunde: „Wer hat diese Rechnung gelöscht?“ Dann öffnest du eine Tabelle mit 40.000 Zeilen und Spalten wie event_type und payload. Eine Audit Log UI, mit der Menschen wirklich arbeiten können, macht aus dieser Tabelle Sätze: Jessica Dunn hat Rechnung #2026-0398 storniert, gestern um 16:35 Uhr, mit Firefox unter Windows, und der Status ging von Ausgestellt auf Storniert. Dieser Guide behandelt den Screen und die Daten dahinter: Tagesgruppen, Filter, Vorher-nachher-Diffs, CSV-Export, das Layout fürs Smartphone und die Form der Ereignisse, die dein Backend speichern sollte, damit die UI etwas Gutes anzeigen kann.
Gutes Audit Log UI Design entscheidet sich vor allem dann, wenn es Tausende Ereignisse gibt: wie die Filter zum Server kommen, was im Export steht, wie sich ein Diff auf einem 390px breiten Screen liest und was du speicherst, damit eine Zeile mehr sagen kann als „Datensatz aktualisiert“. Das durchgehende Beispiel ist die Komponente Audit Log.
Was zeigt eine brauchbare Audit Log UI in jeder Zeile?
Jede Zeile ist ein Satz aus vier Teilen: wer (Avatar und Name oder eine klare Markierung für das System, einen API-Key oder eine Integration), hat was getan (ein Verb, kein Event-Schlüssel), an welchem Datensatz (mit Link darauf) und wann. Schweregrad und IP bekommen eigene Spalten, weil man bei einer Untersuchung nach ihnen filtert und beim Überfliegen auf sie achtet. Alles andere (Gerät, Standort, Request-ID, die geänderten Felder) steht in der aufgeklappten Zeile, denn es zählt immer nur für ein einzelnes Ereignis.
Drei Gewohnheiten entscheiden, ob Leute ein Log lesen oder es aus Frust in eine Tabellenkalkulation exportieren:
- Tagesüberschriften. „Heute“, „Gestern“, „Montag, 21. September“ als Sticky-Überschriften, dazu die Uhrzeit in jeder Zeile. Die absolute Zeit bleibt lesbar, ohne dass jemand mit der Maus darüberfahren muss.
- Verben aus einem Wörterbuch. Speicher
invoice.cancelledin der Datenbank und mach in der UI daraus „storniert“, mit Icon und Farbton (tone). Der Schlüssel bleibt stabil, wenn sich der Text ändert. - Gewichtung nach Risiko. Wenn jede Zeile gleich aussieht, verschwinden der Rollenwechsel und die fehlgeschlagenen Anmeldungen zwischen Hunderten Routineänderungen. Zurückhaltender Stil für Info, Soft Badges für Notice, Warning und Critical.
Wie renderst du ein React Audit Log?
Installier die Komponente (sie bringt Tabelle, Filter Builder, Zeitraumauswahl und CSV-Helfer gleich mit):
Dann übergibst du Ereignisse und ein Wörterbuch für die Aktionen. Die Map actions macht aus Schlüsseln Verben, Icons und Farbtöne, und group sortiert sie in der Filterliste:
Ohne weitere Props bekommst du eine Suche (Akteur, Satz, Datensatz und IP, Akzente werden ignoriert), eine Zeitraumauswahl mit Schnellbereichen, Filter-Chips für Person, Aktion, Datensatztyp, Schweregrad und IP, Sticky-Tagesüberschriften, aufklappbare Zeilen und einen CSV-Button. Probier es unten aus.
$ npx wingo-ui@latest add audit-logWelche Filter braucht eine Audit Trail UI?
Wer etwas untersucht, stellt enge Fragen: alles, was Liam letzten Dienstag getan hat, jede Löschung in diesem Monat, jedes kritische Ereignis von einer bestimmten IP. Die Filter sind das wichtigste Werkzeug auf diesem Screen, deshalb decken die Standards genau diese Fragen ab:
Gespeicherte Ansichten machen aus den häufigen Fragen einen einzigen Klick. Übergib views mit einem Namen und einer Filtergruppe, gebaut mit createFilterGroup und createFilterCondition aus lib/filters (dasselbe Modell, das auch der Filter Builder nutzt):
Wie zeigst du in einem Audit Log, was sich geändert hat?
Speicher die geänderten Felder, keine Beschreibung als Freitext. Jedes Ereignis trägt eine Liste changes aus { field, label, from, to, type }, und die Zeile klappt zu einer zweispaltigen Vorher-nachher-Tabelle auf. Der type bestimmt die Formatierung: money gibt $7,315.20 in der Währung des Logs aus, date ein Datum, boolean Ja oder Nein, status ein eingefärbtes Badge, und ein leerer Wert erscheint gedämpft, so erkennt man „war leer, jetzt gesetzt“ auf einen Blick.
Dieselbe Tabelle wird als AuditDiff exportiert, falls du sie in der Seitenleiste eines Datensatzes oder in einem Bestätigungsdialog brauchst. Hat sie weniger als 448px Platz, stapelt sie die Zeilen. So bleibt der Diff auf dem Smartphone lesbar, statt zwei Spalten zusammenzuquetschen.
Bei langem Text stoßen Feld-Diffs an ihre Grenzen: eine Vertragsklausel, ein E-Mail-Template, ein Richtliniendokument. Zwei Werte mit je 2.000 Zeichen nebeneinander in einer Tabellenzelle helfen niemandem. Leg eine Versions-ID in metadata ab, verlinke den Datensatz mit seiner Versionsseite und zeig die beiden Versionen in einem Diff Viewer mit Hervorhebung auf Wortebene. Der Guide zum React Diff Viewer behandelt diese Komponente ausführlich.
$ npx wingo-ui@latest add diff-viewerWelche Form sollten die Ereignisse im Backend haben?
Eine Append-only-Zeile pro Ereignis, geschrieben in derselben Transaktion wie die Änderung, die sie beschreibt. Wenn das Schreiben ins Log fehlschlagen kann, während die Änderung durchgeht, ist das Log unvollständig, und niemand kann ihm trauen. Eine Postgres-Tabelle, die eins zu eins auf AuditEvent passt:
Ein paar Entscheidungen hinter dieser Form:
- Kopier den Namen des Akteurs und das Label des Ziels. Menschen werden umbenannt, Datensätze gelöscht. Das Log muss festhalten, was zum Zeitpunkt des Ereignisses galt, und darf deshalb nicht von einem Join abhängen.
- Halte den Aktionsschlüssel maschinenlesbar. Schlüssel nach dem Muster
resource.verbgruppieren sich von selbst (der Filter für den Datensatztyp der Komponente greift ersatzweise auf den Teil vor dem Punkt zurück) und überstehen Textänderungen. - Lass Geheimnisse aus
changesheraus. Das OWASP Logging Cheat Sheet (opens in a new tab) (gelesen im November 2026) nennt Passwörter, Access Tokens, Session-IDs, kryptografische Schlüssel, Kartendaten und sensible personenbezogene Daten unter den Dingen, die du vor dem Loggen entfernen, maskieren oder hashen solltest. Bei einer Passwortänderung loggst du{ field: "password", from: null, to: "changed" }und sonst nichts. - Protokolliere wichtige Lesezugriffe. Exporte, das Ansehen fremder Nutzerdaten und Zugriffe durch den Support gehören auch ins Log, nicht nur Schreibvorgänge.
Wie paginierst und filterst du ein großes Audit Log auf dem Server?
Im Standardmodus mode="client" durchsucht und filtert die Komponente die Ereignisse, die du übergibst. Das passt für ein begrenztes Zeitfenster wie die letzten 90 Tage. Sie rendert höchstens maxRows Ereignisse (standardmäßig 200) und weist unter der Liste darauf hin. Ein großes Array auf dem Client kostet dich also trotzdem den Download und das Filtern. Wächst die Tabelle darüber hinaus, wechselst du zu mode="server": Die Komponente filtert nicht mehr selbst und ruft onQueryChange 300ms nach der letzten Änderung an Suche, Zeitraum oder Filter auf, mit { search, period, filter }. Du holst die Daten und gibst die Seite mit totalCount, hasMore und onLoadMore zurück. refreshing lässt die Zeilen sichtbar und zeigt einen dünnen Fortschrittsbalken, solange die Anfrage läuft.
Auf dem Server liest parseFilter den Filter wieder ein, und du übersetzt seine Bedingungen (die Felder actor, action, type, severity, ip) in eine parametrisierte where-Klausel. Paginiere mit einem Cursor auf (at, id) statt mit einem Offset, damit neue Ereignisse, die oben hinzukommen, die Seiten nicht verschieben.
Wie sollte der CSV-Export funktionieren?
Der eingebaute Button lädt die Ereignisse herunter, die zur aktuellen Suche, zum Zeitraum und zu den Filtern passen, als audit-log-2026-11-06.csv. Zellen, die mit =, +, - oder @ beginnen, bekommen ein Präfix, damit eine Tabellenkalkulation sie nicht als Formel ausführt, und mit csvDelimiter=";" öffnet sich die Datei korrekt in Excel mit europäischen Regionseinstellungen. Die Zeitspalte nutzt die Ortszeit der Person, die exportiert, auf die Minute genau.
Für „schick mir, was ich gerade sehe“ ist das richtig. Für einen Security-Fragebogen ist es die falsche Datei: Der verlangt jedes passende Ereignis, in UTC, mit Sekunden. Übergib onExport: Es bekommt die sichtbaren Ereignisse und die aktuelle Abfrage, und ein zurückgegebenes Promise versetzt den Button in den Ladezustand, während dein Server die Datei baut. Protokolliere den Export selbst als Ereignis (audit_log.exported), denn ein Download der gesamten Historie ist genau die Art von Lesezugriff, die ein Reviewer sehen will.
Wie sieht ein Audit Log auf dem Smartphone aus?
Unter 768px wird die Tabelle zu einem Feed aus Tagesgruppen. Jede Zeile zeigt den Satz und die Uhrzeit; ein Tipp klappt Diff und Details direkt darunter auf, der Link zum Datensatz wandert in die Details, und Zeitraum und Filter öffnen sich als Bottom Sheets. „Mehr laden“ wird zu einem Button über die volle Breite oder lädt mit autoLoadMore beim Scrollen nach. Das folgt derselben Regel wie jede responsive Tabelle: Auf dem Smartphone werden Zeilen zu Karten, statt seitlich zu scrollen.
Wann nimmst du stattdessen eine Timeline oder eine Data Table?
Nimm eine Timeline für die Historie eines einzelnen Datensatzes: das Aktivitäts-Panel auf einer Kundenseite, wo sich zwanzig Ereignisse eines Kunden als vertikale Geschichte mit Icons besser lesen als in einer filterbaren Tabelle. Speis sie aus denselben Zeilen von audit_events, gefiltert nach target_type und target_id (dafür ist der zweite Index da).
Nimm eine Data Table, wenn es auf dem Screen vor allem um Spalten geht: Sortieren nach beliebigen Feldern, Zeilen auswählen, Spalten ausblenden. Sie ist das allgemeine Werkzeug; das Audit Log ist das spezialisierte, mit Sätzen, Diffs und Tagesgruppen schon eingebaut. Wenn du eine Event Log UI für rohe Anwendungslogs brauchst (Textzeilen mit Leveln, die live hereinlaufen), passt der Guide zum Log Viewer besser.
Wenn du den ganzen Admin-Screen fürs Team baust, zeigt das Tutorial zur Rollenverwaltung das Audit Log als einen Tab neben Mitgliedern und einer Berechtigungsmatrix, alles im Block Users Admin Page. Weitere Guides dieser Art findest du in der Kategorie Komponenten-Guides, zum Einstieg etwa den Guide zu React-Formularkomponenten. Wenn Leute Bedingungen kombinieren müssen (dieser Nutzer UND dieser Datensatztyp, ODER jedes Löschen), behandelt der Guide zum React Filter Builder AND/OR-Chips und gespeicherte Ansichten.
Wie geht es weiter?
Fang mit der Ereignistabelle an: Schreib pro Änderung eine Zeile in derselben Transaktion, mit dem Namen des Akteurs, einem Aktionsschlüssel, dem Ziel und den geänderten Feldern. Steht das, ist der Screen größtenteils Konfiguration. Audit Log, Diff Viewer, Timeline und Data Table sind Pro-Komponenten (die Pläne beginnen bei $8 im Monat, siehe Preise). Doku und Demos sind öffentlich, du kannst also jede Prop ausprobieren, bevor du dich entscheidest, und ein kostenloser Account gibt dir den Quellcode der 75 kostenlosen Items.
Häufige Fragen
Was gehört in einen Audit-Log-Eintrag?
Der Akteur (Person, API-Key oder Integration), ein Aktionsschlüssel wie invoice.cancelled, der betroffene Datensatz mit Typ und ID, ein exakter Zeitstempel, die geänderten Felder mit Vorher- und Nachher-Werten und der Request-Kontext: IP, User Agent und eine Request-ID. Der Schweregrad ist optional, macht die Suche nach riskanten Ereignissen aber deutlich schneller.
Was ist der Unterschied zwischen einem Audit Log und einem Activity Feed?
Ein Activity Feed dient der Abstimmung im Team und darf kleine Ereignisse zusammenfassen oder weglassen. Ein Audit Log ist ein vollständiges Append-only-Protokoll, auf das sich ein Security-Reviewer oder ein Kunde verlassen kann. Es braucht deshalb exakte Zeiten, eine eindeutige Zuordnung, Filter und einen Export.
Sollte ein Audit Log relative Zeiten wie „vor 5 Min.“ zeigen?
Für die Zeilen von heute sind relative Zeiten in Ordnung, solange die absolute Zeit in der Nähe sichtbar bleibt, zum Beispiel unter einer Tagesüberschrift. Für Prüfungen und Exporte zeigst du Datum und Uhrzeit vollständig und nennst die Zeitzone.
Wie zeige ich ein Audit Log auf dem Smartphone an?
Mach aus der Tabelle einen Feed aus Tagesgruppen, in dem jede Zeile ein Satz ist (Akteur, Verb, Datensatz) mit der Uhrzeit. Ein Tipp klappt Diff und Details direkt darunter auf. Filter und Zeitraumauswahl öffnest du als Bottom Sheets statt als Popover.
Sollte der CSV-Export im Browser oder auf dem Server laufen?
Für die bereits geladenen Zeilen, etwa die letzten 90 Tage, reicht der Browser. Einen vollständigen Compliance-Export baust du auf dem Server, damit er jedes passende Ereignis enthält, UTC-Zeitstempel nutzt und selbst als Audit-Ereignis protokolliert wird.
- Audit Log
- Admin
- Sicherheit
- B2B SaaS
- React
