![]() |
Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Hallo zusammen,
ich bin wieder dabei ziemlich tief in fremde Codes per Logging abzutauchen und große Log-Sitzungen auszuwerten. Was mich stört ist das sehr schlechte Handling des Loggings unter Delphi. Leider habe ich noch nicht die perfekte oder zumindest akzeptable Log-Lösung für mich gefunden. Unter C++ habe ich sehr vielseitige Logger als Makros, was aber unter Delphi leider nicht funktioniert.. Ich müsste unter Delphi dann NULL-Logger anlegen, der Messages in Leere laufen lässt, aber letztendlich würde der ganze Code doch in der App bleiben, oder nicht ? Vielleicht ist der Delphi Compiler/Linker clever genug um das rauszurechnen, vielleicht gibt es auch Tricks mit NULL-Logger und Inline Funktionen, ich bin da für Ideen und Vorschläge offen. Den C++ Makros am nächsten käme eventuell eine Assert-basierte Logging Lösung
Es gibt sicher für und wider für eine solche Assert-basierte Lösung, z.B.
Meine Ziele dafür wären möglichst eine zentrale Verwaltung, möglichst gruppierbar (in den Units oder nach Gruppennamen), es sollte sich natürlich auch unter CrossPlatform gleich verhalten. Vielleicht könnt Ihr mir da noch etwas auf die Sprünge helfen, um eine optimalere Lösung hinzubekommen ? |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
|
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Ja dankesehr, Grijjy kenne ich auch.
Der ist schon nahe an Perfekt, weil es eine ganze Infrastruktur bietet. Ich denke aber das alle Logger die ich bis jetzt gesehen habe im Prinzip ähnlich funktionieren. Was ich meine ist, das Senden einer Message:
Delphi-Quellcode:
wir mehr oder weniger immer so gemacht.
XyzLog.Send('Sample Info Message', TgoLogLevel.Info);
Ich möchte aber genau diesen Code mal aktive, und mal inaktiv schalten können.
Delphi-Quellcode:
Das würde wohl funktinieren, ist aber rein optisch nicht so mein Favorit :stupid:
Uses XyzLog;
// Globales abschalten möglich, z.B. über .inc Datei, wenn _X_LOG undefiniert ist // Globales Abschalten bei RELEASE {$IF DEFINED( _X_LOG) and DEFINED(DEBUG) } {$UNDEF _X_LOG} // remove the global, and handle the local scope {$DEFINE _X_LOG} // Lokales, Unit-scope Abschalten hier möglich {$ENDIF} // Das ginge z.B. so // Spezielles Abschalten ist hier möglich {$IFDEF _X_LOG} XyzLog.Send('Sample Info Message', TgoLogLevel.Info); {$ENDIF} Ich suche da eigentlich nach einer eleganteren Version ohne zu viele ifdefs (falls es die gibt) |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Und wenn du die IFDEFS innerhalb der Send/Log-Methoden machst (ggf. den Logger der Wahl einfach wrappen)?
|
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Ja die LogFunktionen liegen in einer anderen Unit.
Da kann ich globale Features gruppieren, etc., muss aber immer mit .inc Dateien hantieren. Schöner fände ich das im Kopf einer Unit. Was dann aber trotzdem passiert, ist das Aufrufen evtl. leerer Funtionen, den Funktion-Call würde ich natürlich auch gerne im RELEASE wegbekommen. Man könnte evtl. auch was mit Mocks machen, aber ich meine die sind dafür auch nicht optimal. Es geht z.B. um Folgende Situation: Ich muss ein Problem debuggen, und baue dafür an zig Stellen in einer/mehreren Units Logs ein. Wenn das läuft müsste ich 1. entweder alle Logs wieder rausbauen 2. oder die Logs einfach "ausschalten" (und möglichst NUR diese) Ich möchte halt 2tes Erreichen, auch weil ich die Logs für spätere Tests noch drinbehalten möchte. Mit Asserts wäre das z.B. möglich, denn man kann im Kopf einer Unit Assertions ein/ausschalten
Delphi-Quellcode:
und damit wäre der Aufruf der Asserts weg (keine leere Funktion wird benötigt)
$ASSERTIONS ON/OFF
|
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Ich nehme an, Du meinst bei der Assertion-basierten Lösung sowas wie:
![]()
Delphi-Quellcode:
Assert(False, 'Dies ist eine Logmeldung');
Ich habe das mal ausprobiert und bin da nicht so der Fan: Vorteil Assertion: * Man kann das Logging recht einfach komplett ausschalten * Man kommt einfach an die Zeilennummer der Assertion, in der die Log-Message geschrieben wird. Nachteil Assertion: * Wer es nicht kennt, den verwirrt diese Verwendung von Assert. * Assertions sind ziemlich CPU-Intensiv, d.h bei eingeschaltetem Logging ändert sich das Zeitverhalten stark. * Man kann Assertions nicht mehr anderweitig verwenden. Persönlich bevorzuge ich die NULL-Logger-Lösung. Es ist einfach sauberer. |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Zitat:
Ich meine wenn Du dort per ifdef den logging code ausklammerst und die Methode noch als inline deklarierst - würde der Compiler die dann nicht wegoptimieren? |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Zitat:
Delphi-Quellcode:
procedure Main;
var T: TLog; begin T.Init('TLogConsole'); T.Log('Hallo Welt'); Writeln('Wie geht''s?'); Readln; end;
Delphi-Quellcode:
unit uLogging;
interface {.$DEFINE Log} type ILog = interface ['{76BA1F8A-D171-4817-BB3D-337295B03CE0}'] procedure Log(AText: string); end; TLog = record private {$IFDEF Log} FLog: ILog; {$ENDIF} public procedure Init(const AName: string); inline; procedure Log(AText: string); inline; end; implementation type TLogConsole = class(TInterfacedObject, ILog) public procedure Log(AText: string); end; procedure TLogConsole.Log(AText: string); begin Writeln(AText); end; procedure TLog.Init(const AName: string); begin {$IFDEF Log} // der Einfachheit halber FLog := TLogConsole.Create; {$ENDIF} end; procedure TLog.Log(AText: string); begin {$IFDEF Log} if FLog <> nil then begin FLog.Log(AText); end; {$ENDIF} end; end. |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Ich persönlich bin ja Fan von dem Ansatz, den CodeSite, SmartInspect o.ä. verfolgen. Da bleibt der Aufruf grundsätzlich auch in nem Release Build drin, ist aber so geschrieben, dass er wenigstmöglich weh tut, wenn das Logging ausgeschaltet ist. Da hat den Vorteil (tun wir bei unserem Produkt täglich), dass man dem Kunden das mal kurz anschalten lassen kann, er schickt die Logdateien und der Support und/oder Entwicklung kann das auswerten.
Asserts gehören mMn ebenso wie Range und Overflow Checking im Release ausgeschaltet. Eine wirklich gute Lösung für Logging hat man mMn sowieso nur, wenn die Sprache nativ AOP kann oder so Dinge wie PostSharp möglich sind. |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Ich bin auch immer für ein zur Laufzeit aktivierbares Logging. Erleichert die Arbeit beim Kunden. Um es möglichst ressourcen-schonend umzusetzen ist dann wichtig, dass man zumindest für den Level INFO und DEBUG zuerst per boolean prüft, ob geloggt werden soll, bevor der zu loggende String angelegt wird. Generell nutze ich TMSLogging und je nach OS (z.B. Android) zusätzlich Log.d(''), damit es in den Prelaunch-Reports auftaucht.
Code:
if (myLog.isInfoEnabled()) then
begin {$IFDEF ANDROID} Log.d('INFO: XXXXXXX'); {$ENDIF} myLog.info('XXXXXXXX'); end; |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Interessanter Thread! Was ich mich bei all den Lösungen hier gefragt habe ist: Wie schaut es mit Multi-Threading aus? Gerade dabei würde ich mir oft eine einfache Möglichkeit des Loggings wünschen, ohne kompliziert erst Messages zu definieren, HWNDs überall hin durchzureichen, an gewünschten Stellen solche Events auszulösen und mit l/wParam-Pointer-Missbrauchten handgestreichelten Strings zu hantieren. Möglichst ohne allzuviel Aufwand eine solche Lösung in bestehende Projekte zu bringen, die nicht spezifisch darauf hin erstellt wurden.
|
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Dankesehr für die Vorschläge und Kommentare.
@dummzeuch Ja, genau das meine ich (Assert- oder Assertion-basiert, mir fehlt da der richtige Bezeichner). Das mit Vor- Nachteil sehe ich ähnlich. Es könnte da auch noch den Nachteil geben mit vorhandenen Asserts (in Library oder 3rd Party), das könnte dann womöglich mal in die Hose gehen. Ich bin ja Mental auch eher bei einem NULL-Logger. @freimatz @Uwe Raabe Ja das hatten wir schonmal so ähnlich, aber nicht als komplette Infrastruktur. Inline und Interfaces können helfen, könnte eine lokale Gruppierung dann so aussehen ?
Delphi-Quellcode:
@Stevie
// Das könnte global in einem Log.Types Unit liegen, zur zenralen Steuerung, oder
// dort auch per ifdef's gesteuert const CLog_Group_1 = 1; // per 0/1 individuell abschaltbar CLog_Group_2 = 0; // per 0/1 individuell abschaltbar CLog_Group_3 = 1; // per 0/1 individuell abschaltbar procedure Main; var T: TLog; begin T.Init('TLogConsole'); // Hierbei kann zwar Log weggeschalted werden ,aber nicht die Groups zentral verwaltet werden T.Log(CLog_Group_1, 'Hallo Welt'); T.Log(CLog_Group_2, 'Hallo Welt'); T.Log(CLog_Group_3, 'Hallo Welt'); // So könnte es gehen, wenn die Groups im Log z.B. als class var verwaltet werden. T.LogGroup_Enable(CGroup1, CLog_Group_1); T.LogGroup_Enable(CGroup2, CLog_Group_2); T.LogGroup_Enable(CGroup3, CLog_Group_3); // T.LogGroup_1('Hallo Welt'); T.LogGroup_2('Hallo Welt'); T.LogGroup_3('Hallo Welt'); // Nachteil: TLog müsste eine Menge über die Module wissen, damit könnte ich aber womöglich Leben end; Könnte interessant sein, muss ich mal checken. Mit denen habe ich mich nur theoretisch beschäftigt, aber ich denke CodeSite ist nicht FMX/Mobile: das wäre ein NoGo für mich. Leider finde ich sehr wenig konkrete Daten darüber. SmartBear dito Zitat:
Wie genau machen die das denn im Code, ich vermute mal das es so eine Art "Sprungmarken" dort gibt, als Pointer zu den eigentlichen LogFunktionen. Sorry, ich habe keines der Beiden installiert. Oder könnte das etwa der gleiche Ansatz mit Interfaces sein, und je nach Anforderung auf NullInterfaces zeigen lassen ? @Phillip Hoffman Ja das wäre auch eine schöne Option, würde ich auch gerne unter Gruppierung sehen. Ich denke dabei wird dan immer ExtraCode in den Units bleiben, aber gut für PreRelease Tests. Ich mache sowas teilweise mit zuschaltbarem Logging, in einem speziellen Debug-Modus meiner Apps, die dann lokale Logs anzeigen können. Damit kann ich sogar per Hotline mit den Kunden Einzelfälle besser debuggen. @Medium Im Prinzip ist MultiTread kein Problem (höchstens der zusätzliche Overhead und Performaceverlust). Nur die Anzeige der Logs muss im UI-Thread, oder noch besser extern erfolgen. Schau dir mal ein Android Logcat von einem Phone an, das sended andauernd zig Logs, auch wenn im Frontend gar nichts passiert, da kommt Einiges aus Threads. |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Zitat:
Es ging aber darum, dass man nix über Defines raus nimmt sondern einfach den Log Aufruf im Code hat, in diesem aber nichts weiter tut, wenn der Logger nicht aktiviert ist. Sollte der Logaufruf schon potenziell zu teuer (messen, nicht raten!) sein, dann kann man einen Ansatz nutzen, wie in Phillip in #10 beschrieb. |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Ja sorry, typo beim Lesen :oops:
Trotzdem SmartInspect scheint auch nicht für Mobile geeignet zu sein. Wenn ich erst tief in Google nach solchen Features suchen muss, ohne Ergebnis, scheint es bei Beiden nicht drin zu sein. Die ViewerOberflächen scheinen ja Einiges zu können, aber da muss man sich erstmal reinarbeiten. Vielleicht bleibe ich erstmal bei Grijjy/Logcat, damit kenne ich mich besser aus, aber ich werde die Beiden mal bei Gelegenheit genauer anschauen was deren Unterschiede ausmacht. Ja, ich checke gerade einen Ansatz mit if, wie ihn Phillip beschrieben hat, aber zusammen mit dem Interface/Inline Ansatz. Mein Ziel wäre es die Gruppen für verschiedene Module möglichst zenrral zu steuern, und es könnte in diese Richtung gehen. Vielleicht könnten die Gruppen sich selbst registieren, in der initialization section. Auch das Logging komplett abschalten, über Defines in einer Log.pas, könnte ich dann immer noch. |
AW: Optimales Logging: Assert-basiert oder NULL-Logger: Ja oder besser nicht ?
Zitat:
Verschiedene Messages für verschiedene Meldungen sind noch schlimmer, da dann jede einen eigenen Handler braucht. Man spart sich dann zwar das Char/String Gehampel da man einfach die Texte im Handler anlegt (oder besser ressourcestrings nutzt), aber das ist für jede neue Log-Art ein ziemlicher Aufriss, und die Fensterklasse besteht nachher zu 50% aus Log-Handlern. Nichts um mal ad hoc einen Bug zu finden. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:31 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz