Skip to main content

Logging

Serverseitiges Logging​

Serverseitig wird im jadice web toolkit das Logging der jadice document platform 5 eingesetzt, das das Anbinden unterschiedlicher Frameworks erlaubt.

Eine ausführliche Beschreibung kann der Dokumentation der jadice document platform 5entnommen werden.

Kontext im Log (MDC)​

Der Server setzt für jede Nachricht und jede Kachelanfrage drei MDC-Schlüssel, die sich in das Log-Pattern aufnehmen lassen:

MDC-SchlüsselInhalt
jwt.clientIdTransport-Client-ID der Viewer-Sitzung
jwt.docIdentifierKennung des gerade bearbeiteten Dokuments
jwt.userIdBenutzer hinter der Sitzung, sofern eine Integration ihn registriert hat (siehe unten); sonst leer

Beispiel für Logback (application.yml einer Spring-Boot-Anwendung):

logging:
pattern:
console: "%d %-5level [%thread] [clientId:%X{jwt.clientId}] [user:%X{jwt.userId}] [docId:%X{jwt.docIdentifier}] %logger: %msg%n"

MDCFilter räumt die Schlüssel am Ende jeder Anfrage wieder ab (siehe Servlet-Unterstützung).

Benutzer je Viewer-Sitzung: ClientUserRegistry​

Das jadice web toolkit kennt die Benutzer der integrierenden Anwendung nicht. Damit Log-Zeilen trotzdem einem Benutzer zugeordnet werden können, gibt es die com.levigo.jadice.web.transport.server.session.ClientUserRegistry: eine serverweite Zuordnung von Transport-Client-ID zu einer Benutzerkennung, die die Integration setzt, sobald sie den Benutzer kennt. Der Transport trägt die Kennung danach bei jeder eingehenden Nachricht und Kachelanfrage dieser Sitzung als jwt.userId ins MDC ein.

// z. B. in einem DocumentDataProvider, einem MessageListener oder einem eigenen Servlet-Filter,
// sobald der Benutzer bekannt ist (Session, Token, Header):
ClientUserRegistry.getInstance().register(context.getClientId(), userId);

Eigenschaften:

  • Nur Logging-Kontext. Die Registry prüft oder vergibt keine Rechte; Autorisierung bleibt Sache der Integration.
  • Lebenszyklus. Ein Eintrag wird beim Schließen der Transport-Sitzung entfernt. Sitzungen, die ohne Abmeldung verschwinden (Tab geschlossen, Netz weg), verfallen nach zwei Stunden ohne Zugriff; abgelaufene Einträge werden beim nächsten Zugriff bzw. bei der nächsten Registrierung entsorgt.
  • Leere Werte werden ignoriert, jwt.userId bleibt dann leer. ClientUserRegistry.mdcValue(userId) liefert den MDC-tauglichen Wert (nie null).
  • Personenbezogene Daten. Die Kennung landet in jeder Log-Zeile der Sitzung; Aufbewahrungsfristen und Zugriff auf die Logs sind entsprechend zu regeln.

Die ICN-Anbindung nutzt die Registry, um den ICN-Benutzer aus dem signierten Token (sub) zu übernehmen; sie schreibt ihn zusätzlich als Dokument-Property server.icn.user.

Clientseitiges Logging​

Auf der Clientseite wird das Logging auf das von GWT unterstützte java.util.logging umgeleitet. Die Logging-Konfiguration kann in den jeweiligen .gwt.xml Dateien der Module modifiziert werden:

  • Loglevel setzen: <set-property name="gwt.logging.logLevel" value="SEVERE"/>. Hier können die Loglevel von java.util.logging verwendet werden, die das Webtoolkit unterstützt: FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE Als Default ist im jadice web toolkit WARNING gesetzt.

  • Console- und RemoteHandler aktivieren: <set-property name="gwt.logging.consoleHandler" value="ENABLED"/>. consoleHandler und simpleRemoteHandler können über das Attribut ENABLED bzw. DISABLED aktiviert bzw. deaktiviert werden. Per Default ist im jadice web toolkit der consoleHandler aktiviert und der simpleRemoteHandler deaktiviert.

  • Falls GWT Logging bereits genutzt wird, kann integrationsseitig das im JWT voreingestellte Loglevel durch folgende Reihenfolge in der .gwt.xml Datei überschrieben werden: `

    `

In der gewünschten Klasse kann nun über Logger logger = LoggerFactory.getLogger(classname.class) ein Logger instanziiert werden. Dieser übernimmt die Konfigurationen der zugehörigen .gwt.xml Datei. Über verschiedene Methoden können dann Messages auf verschiedenen Levels geloggt werden:

Methodejava.util.logging Level
logger.trace(String message, Throwable t)FINEST
logger.debug(message, t)CONFIG
logger.info(message, t)INFO
logger.warn(message, t)WARNING
logger.error(message, t)SEVERE

Der Parameter t ist dabei optional.

Weitere Informationen können unter GWT Logging gefunden werden.

Remote-Logging (Client → Server)​

Clients können Log-Meldungen über die Transport-Verbindung an den Server senden (Nachricht LOG_REMOTE); beim GWT-Client geschieht das bei aktiviertem simpleRemoteHandler (s.o.). Serverseitig nimmt der RemoteLoggingMessageListener die Meldungen entgegen und schreibt sie mit dem übermittelten Loglevel in das Server-Log. Ein mitgesendetes Throwable wird dabei serverseitig rekonstruiert.

Inhalt, Loglevel und Client-ID solcher Meldungen sind vollständig vom Client kontrolliert. Der Server härtet die Verarbeitung deshalb wie folgt ab:

  • Sanitisierung (Log-Injection, CWE-117): Steuerzeichen einschließlich Zeilenumbrüchen (CR/LF) werden durch Leerzeichen ersetzt. Eine Meldung belegt damit immer genau eine Log-Zeile und kann keine fremden Log-Einträge vortäuschen. Meldungen werden auf 4096 Zeichen gekürzt (Markierung …[truncated]). Jede Meldung erhält das Präfix [remote-client:<clientId>]; die Client-ID wird ebenfalls sanitisiert.
  • Drosselung (Flooding): Pro Client-ID werden höchstens 100 Meldungen pro Sekunde verarbeitet. Darüber hinausgehende Meldungen werden verworfen; der Client erhält dennoch eine Bestätigung, der Fire-and-forget-Vertrag bleibt also erhalten. Die Grenzwerte sind nicht konfigurierbar.
  • Begrenzte Throwable-Rekonstruktion: Mitgesendete Exceptions werden auf 25 verkettete Ursachen (cause) und 1024 Stacktrace-Elemente begrenzt; ein fehlender Stacktrace wird toleriert. Fehler bei der Verarbeitung, auch Error-Instanzen wie StackOverflowError, werden abgefangen und beenden nicht den Worker-Thread.
  • Abschaltbar: Über ConfigurationManager.getServerConfiguration().setRemoteLoggingEnabled(false); (Spring-Property webtoolkit.remoteLoggingEnabled, Default true) lässt sich die serverseitige Verarbeitung vollständig deaktivieren. Remote-Meldungen werden dann quittiert, aber nicht ins Log geschrieben.

Sanitisierung und Drosselung greifen unabhängig von diesem Schalter immer. Integrationen, die kein Remote-Logging nutzen, sollten den Schalter in Produktion auf false setzen: Das Server-Log enthält dann keine vom Client frei wählbaren Inhalte (Relevanz für DSGVO Art. 32 und Log-Integrität nach ISO 27001).

Beispiel application.yml:

webtoolkit:
remoteLoggingEnabled: false