Ein Versuch über einen Protokoll-Service

Ein Versuch über einen Protokoll-Service

Mein Salesforce Samstag: Wünsch Dir was...

Ich mag Debug Logs, aber so richtig glücklich machen sie nicht. Es ist keine große Kunst, die 2 MB Grenze zu überschreiten. Bei größeren Applikationen sind sie keine Hilfe und gerade Einsteigern bereiten sie Kopfzerbrechen, weswegen system.debug(myObject__c.Name) in Apex noch ganz weit vorne mitspielt.

Wenn es mir aussuchen könnte, würden Logs eher so aussehen:

Und so auch...

Falls die Zahlen irritieren: Die geben Zeilen und Spalten der beteiligten Klasse an. In größeren Kontext würde ich mir ein Log dann so wünschen:

So hab ich mir das gedacht

Und zur Laufzeit in der Developer Console so:

Developer Console

Die Hürden...

... der Platform

  • Speichern von Logs im Kontext von try { //... } catch (exception ex) {}-Blöcken
  • Execution Time / Kontext
  • Data Volume / Datenspeicher

Vor Platform Events standen Protokoll-Services vor der Herausforderung, daß das Speichern der Logs wegen eines Database.rollback(Savepoint) gar nicht erst zustande kam.
Mit Plaftform Events stellt sich diese Herausforderung nicht mehr. Einmal abgeschickt, kommt keine Exception mehr in die Quere.

Beim Thema Execution Time hilft nur eines: einschränken oder abschalten können. Egal, wie ein Protkoll Service arbeitet, er wird Rechenzeit in Anspruch nehmen. Daher sind Optionen wie log Exceptions only oder do not log at all von Bedeutung. In der Praxis ist ein global-off switch immens wichtig. Wenn's nix zu analysieren gibt, wieso überhaupt Logs in die Console oder Datenbank schreiben und CPU Time verbrauchen? In meiner Developer Sandbox hingegen könnte das eigentlich immer an sein. Mein Wunsch-Service erkennt den Kontext und schreibt nur in Sandboxen mittels system.debug().

Beim Thema Speicherauslastung helfen hingegen board-eigene Mittel. Batch Jobs können gespeicherte Einträge regelmäßig Entfernen, wenn der Service extensiv genutzt wird. Mit simplen Workflows können Log-Informationen via Outbound Message an externe Endpoints übertragen werden. Vielleicht nicht so sexy wie Platform Events, dafür aber unlimitiert.

... für Anwender

Admins

Admins sollten einfach nachvollziehen können, was passiert ist und sich nicht erst durch komische Textwüsten wie die der Developer Console graben müssen. Eine übersichtliche Darstellung tut not, in der aber alles relevante, z.B. für einen Support-Case, zu entnehmen ist.

Entwickler

Die Menge an Zeilen, die so ein Protkoll-Service in meinem Code einnehmen darf, muß minimal sein. Am einfachsten wäre, wenn ich gar nicht erst darüber nachdenken müßte. Der Kontext aus Klassen/Methodennamen wird automatisch erkannt. Wer tippt das schon gern von Hand?

Um ein komplexes Trace wie oben aus der Developer Console zu erreichen, wäre nicht mehr zu tun als:

/*
*   EXAMPLE CLASS FOR LOGGER
*/

public with sharing class Handler {
    public Handler() {
        Logger.push();
        routine1();
        routine2();
        Logger.pop();
    }

    public void routine1(){
        Logger.push();
        Logger.log('entering subroutine1');
        subroutine1();
        Logger.pop();
    }

    public void routine2(){
        Logger.push();
        subroutine2();
        Logger.pop();
    }

    public void subroutine1(){
        Logger.push();
        Logger.pop();
    }

    public void subroutine2(){
        Logger.push();
        subsub1();
        subsub2();
        Logger.pop();
    }

    public void subsub1(){
        Logger.push();
        Logger.log('doing complex stuff');
        subsub2();
        Logger.pop();
    }

    public void subsub2(){
        Logger.push();
        Logger.log('Doing stuff at stacktrace: ' + Logger.stackTrace);
        Logger.pop();
    }
}

Das Ergebnis

Meinen Versuch gibt es hier auf Github im DX Format.

Mit nominell sechs Methoden - Logger.push(), Logger.pop(), Logger.log(), Logger.popAll(), Logger.logException(), Logger.printLog() - kann jede noch so komplexe Klasse in eine gut lesbare Trace-View gebracht werden. De facto reichen bereits zwei Methoden aus - push() und pop(), wie im Beispiel ersichtlich.

Benutze ich den Logger wie im Beispiel bei Ein- und Austritt in wirklich jede Methode, entgeht der Traceview gar nichts mehr. Klassen-, Methodennamen und Zeilen Angaben werden korrekt erfaßt.

Der Logger hört intern mit sendet ein Platform Event aus, wenn der Vorgang abgeschlossen ist (zB indem popAll() aufgerufen wird). CPU Time wird davor ermittelt.

Sofern in AppConfigMain oder AppConfigSupport eingestellt, werden die Logs als DebugEntry__c gespeichert - wahlweise auch nur dann, falls Exceptions auftreten. Eine Lightning Componente visualisert und hierarchisiert mittels lightning:tree und lightning:recordData die gespeicherten Log Daten.

Dank an

Dan Appleman und Adrian Larsson. Ersterem und seinem Buch 'Advanced Apex' verdanke ich die Idee und die push/pop Architektur. Letzterem und seinem Beitrag auf StackExchange eine perfomante Art, Exceptions für den Logger zu nutzen.

Kommentare anzeigen