Modul/Module: DebugLog - speichern und formatieren von Logs

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Imhotheb
Beiträge: 192
Registriert: 10.10.2014 13:14
Computerausstattung: Intel 8086, 640 KB RAM, Hercules Video Adapter, 2 x 5 1/4" 360kb Floppy, MS-DOS 3
Wohnort: Wolfenbüttel

Modul/Module: DebugLog - speichern und formatieren von Logs

Beitrag von Imhotheb »

Hallo Zusammen,

langsam bin ich auf den Geschmack von Modulen gekommen ... hab hier einen Teil meiner Lib mal "umgebaut" ... vielleicht kann es ja jemand brauchen.

Das "Debug" in PB ist ja schön und gut ... nur manchmal möchte ich die Log-Einträge auch speichern oder in der Console ausgeben ... vielleicht sortieren oder weiter verarbeiten ... verschiedene Level verwalten.

Mit den paar Funktionen im Code können solche Log-Nachrichten einfach gepeichert werden.
Gespeichert wird eine ID (laufende Nummer), Zeit/Datum (mit Date()), ein Level (0 bis 255) und die Nachricht selbst
Ausgegeben werden kann über Debug oder Console.
Außerdem kann man Zeit/Datum/ID und Level anzeigen und auch sortieren lassen.

Das ganze sollte unter Windows/Linux/Mac (32Bit und 64Bit) laufen, wurde aber nur unter Windows und Linux getestet.

Weitere Infos im Code.

Und wie immer ...
Fragen/Anmerkungen ... Kritik oder Verbesserungsvorschläge? Immer her damit.

Viel Spass ...

Code: Alles auswählen

; ==================================================
;|                     DebugLog                     |
;|==================================================|
;| Version: V1.0                Created: 06.04.2015 |
;| Type: PB-Include (Module)                        |
;| Author: Imhotheb (Andreas Wenzl)                 |
;| Compiler: PB5.31 32/64 [Win/Lin]                 |
;| Description: save, show and manage logs          |
; ==================================================
;
; DebugLog - speichern, anzeigen und formatieren von Debug-Nachrichten V1.0
; -------------------------------------------------------------------------
;
; OpenConsole() wird für die Consolen-Ausgabe BENÖTIGT
;
;
; Übersicht der Funktionen/Proceduren:
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; * DebugLog(Msg.s, Level.a = 0, Flags.u = 0)
;   >>> speichert eine Nachricht und gibt diese mit Debug und/oder auf der Console aus wenn [DebugLevel <= Level] ist
; * SetDebugLevel(Level.a)
;   >>> setzt den "globalen" DebugLevel
; * SetDebugLogDateFormat(Format.s = "")
;   >>> setzt ein neues Format für das Datum ... Format entspricht einer Maske$ FormatDate()
;   >>> Standart = "[%dd.%mm.%yy]" ... kein Parameter setzt auf Standart zurück
; * SetDebugLogTimeFormat(Format.s = "")
;   >>> setzt ein neues Format für das Datum ... Format entspricht einer Maske$ FormatDate()
;   >>> Standart = "[%hh:%ii.%ss]" ... kein Parameter setzt auf Standart zurück
; * SetDebugLogFlags(Flags.u = 0)
;   >>> setzt Flags die immer benutzt werden wenn keine angegeben sind
; * ShowLastLog(Flags.u = 0)
;   >>> zeigt die letzte gespeicherte DebugLog-Nachricht an
; * GetDebugLog(Level.a = 0, Flags.u = 0)
;   >>> gibt einen formatierten String mit allen Einträgen, die [Level <= NachrichtenLevel] entsprechen, zurück
;   >>> Mehrere Einträge werden mit Chr(10) getrennt
; * GetDebugLogSorted(Sort.b, Order.b = #PB_Sort_Ascending, Level.a = 0, Flags.u = 0)
;   >>> gibt einen formatierten String mit sortierten Einträgen , die [Level <= NachrichtenLevel] entsprechen, zurück
;   >>> Sort kann #DebugLog_Sort_ID, #DebugLog_Sort_Date, #DebugLog_Sort_Level oder #DebugLog_Sort_Msg sein
;   >>> für Order kann #PB_Sort_* verwendet werden (#PB_Sort_Ascending, #PB_Sort_Descending, #PB_Sort_NoCase)
;   >>> Mehrere Einträge werden mit Chr(10) getrennt
; * GetDebugLevel()
;   >>> gibt den "globalen" DebugLevel zurück
; * GetLastLog(Flags.u = 0)
;   >>> gibt einen formatierten String mit dem letzten gespeicherten Eintrag zurück
;
;
; Folgende Flags können verwendet werden:
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; #NoDebugOutput    = Zeigt keine Nachricht mit Debug an
; #ShowConsole      = Zeigt die Nachricht auf der Console an (nur DebugLog und ShowLastlog)
; #ShowLogDate      = Zeigt das Datum zu einem Eintrag an
; #ShowLogTime      = Zeigt die Uhrzeit zu einem Eintrag an
; #ShowMsgID        = Zeigt die ID (laufende Nummer) zu einem Eintrag an
; #ShowDebugLevel   = Zeigt den DebugLevel für einen Eintrag an
; #NoSave           = verhindert das der Eintrag gespeichert wird
; #OnlyDebugLevel   = Nur Einträge anzeigen die gleich dem DebugLevel sind (DebugLevel = Level)
; #StandardFlags    = StandartFlags benutzen ... normalerweise werden die StandartFlags nur benutzt
;                     wenn keine Flags angegeben sind ... so können sie jedoch verknüpft werden
; #NoFlags          = keine Flags verwenden (auch keine StandartFlags)
;
; mehrere Flags können mit | verknüpft werden
;
;



DeclareModule DebugLog
  Enumeration AWLib_DebugLog_Flags
    #NoDebugOutput  = %0000000000000001   ; don't use "Debug" to Display Message
    #ShowConsole    = %0000000000000010   ; show Message in Console, NEEDs OpenConsole()
    #ShowLogDate    = %0000000000000100   ; add Date to Msg
    #ShowLogTime    = %0000000000001000   ; add Time to Msg
    #ShowMsgID      = %0000000000010000   ; add ID to Msg
    #ShowDebugLevel = %0000000000100000   ; add DebugLevel to Msg
    #NoSave         = %0000000001000000   ; don't Save Msg
    #OnlyDebugLevel = %0000000010000000   ; only Show DebugLevel
    #StandardFlags  = %0100000000000000   ; use Flags and StandardFlags
    #NoFlags        = %1000000000000000   ; Dummy, don't use Standard-Flags (no Flags)
  EndEnumeration
  
  Enumeration AWLib_DebugLog_SortFlags
    #DebugLog_Sort_ID     ; sort by ID
    #DebugLog_Sort_Date   ; sort by Date/Time
    #DebugLog_Sort_Time = #DebugLog_Sort_Date ; Date() is saved ... Date and Time are the same
    #DebugLog_Sort_Level  ; sort by Level
    #DebugLog_Sort_Msg    ; sort by Message
  EndEnumeration
  
  Declare DebugLog(Msg.s, Level.a = 0, Flags.u = 0)     ; Flags = AWLib_DebugLog_Flags ... show if MessageLevel >= GlobalLogLevel or use #OnlyDebugLevel
  Declare SetDebugLevel(Level.a)    ; set "Global" DebugLevel
  Declare SetDebugLogDateFormat(Format.s = "")  ; set DateFormat, Standard = "[%dd.%mm.%yy]"
  Declare SetDebugLogTimeFormat(Format.s = "")  ; set TimeFormat, Standard = "[%hh:%mm:%ss]"
  Declare SetDebugLogFlags(Flags.u = 0)         ; set Standard-Flags ... use () or (0) to delete
  Declare ShowLastLog(Flags.u = 0)      ; show last Log-Entry
  Declare.s GetDebugLog(Level.a = 0, Flags.u = 0)   ; get all Log-Entries
  Declare.s GetDebugLogSorted(Sort.b, Order.b = #PB_Sort_Ascending, Level.a = 0, Flags.u = 0)   ; get all Log-Entries, sorted by ID/Date/Level/Msg ... use #DebugLog_Sort_* and #PB_Sort_* for order
  Declare.a GetDebugLevel()     ; get "Global" DebugLevel
  Declare.s GetLastLog(Flags.u = 0)
  
EndDeclareModule

Module DebugLog
  EnableExplicit
  
  Structure AWLib_Debug_MessageData
    ID.i
    Level.a
    Date.i
    Msg.s
  EndStructure
  
  Structure AWLib_Debug_Data
    Level.a
    AWLib_Msg_Level.a
    Separator.c
    Count.i
    StandardFlags.u
    TimeFormat.s
    DateFormat.s
    List MsgData.AWLib_Debug_MessageData()
  EndStructure
    
  Global DebugData.AWLib_Debug_Data
  
  Procedure AWLib_DebugLog_Internal_CheckFlags(*Flags.Unicode)
    ; Check Flags & set New
    
    If *Flags\u & #NoFlags
      *Flags\u = 0
      ProcedureReturn
    EndIf
    If *Flags\u = 0   ; no Flags given
      *Flags\u = DebugData\StandardFlags
    ElseIf *Flags\u & #StandardFlags    ; if #StandardFlags is used
      *Flags\u | DebugData\StandardFlags
    EndIf
    
  EndProcedure
  Procedure AWLib_DebugLog_Internal_ShowLog(Msg.s, Flags.u)
    
    CompilerIf #PB_Compiler_Debugger    ; disable DebugOutput if no Debugger
      If Not Flags & #NoDebugOutput
        Debug Msg
      EndIf
    CompilerEndIf
    If Flags & #ShowConsole
      PrintN(Msg)
    EndIf       
    
    ProcedureReturn #True
  EndProcedure  
  Procedure AWLib_DebugLog_Internal_CreateMsg(*DebugLog_Message.AWLib_Debug_MessageData, Flags.u)
    ;create DebugLog Message with Time/Date/ID and/or DebugLevel    
    
    If Flags & #ShowLogTime Or Flags & #ShowLogDate Or Flags & #ShowMsgID Or Flags & #ShowDebugLevel
      *DebugLog_Message\Msg = ": " + *DebugLog_Message\Msg
      If Flags & #ShowLogTime
        If DebugData\TimeFormat = ""
          DebugData\TimeFormat = "[%hh:%ii:%ss]"
        EndIf
        *DebugLog_Message\Msg = FormatDate(DebugData\TimeFormat, *DebugLog_Message\Date) + Space(1) + *DebugLog_Message\Msg
      EndIf
      If Flags & #ShowLogDate
        If DebugData\DateFormat = ""
          DebugData\DateFormat = "[%dd.%mm.%yy]"
        EndIf
        *DebugLog_Message\Msg = FormatDate(DebugData\DateFormat, *DebugLog_Message\Date) + Space(1) + *DebugLog_Message\Msg
      EndIf
      If Flags & #ShowMsgID
        *DebugLog_Message\Msg = "<ID:" + *DebugLog_Message\ID + ">" + Space(1) + *DebugLog_Message\Msg
      EndIf
      If Flags & #ShowDebugLevel
        *DebugLog_Message\Msg = "{LVL:" + *DebugLog_Message\Level + "}" + Space(1) + *DebugLog_Message\Msg
      EndIf    
    EndIf
    
    ProcedureReturn #True
  EndProcedure  

  Procedure DebugLog(Msg.s, Level.a = 0, Flags.u = 0)
    Protected MsgData.AWLib_Debug_MessageData  
      MsgData\ID = DebugData\Count + 1
      MsgData\Date = Date()
      MsgData\Msg = Msg
      MsgData\Level = Level
      
    AWLib_DebugLog_Internal_CheckFlags(@Flags)
      
    If Not Flags & #NoSave
      DebugData\Count + 1
      AddElement(DebugData\MsgData())
      DebugData\MsgData() = MsgData
    EndIf
    If Flags & #OnlyDebugLevel
      If DebugData\Level = Level
        AWLib_DebugLog_Internal_CreateMsg(@MsgData, Flags)
        AWLib_DebugLog_Internal_ShowLog(MsgData\Msg, Flags)
      EndIf
    ElseIf DebugData\Level <= Level
      AWLib_DebugLog_Internal_CreateMsg(@MsgData, Flags)
      AWLib_DebugLog_Internal_ShowLog(MsgData\Msg, Flags)
    EndIf      
    
    ProcedureReturn #True
  EndProcedure
  Procedure SetDebugLevel(Level.a)
    
    DebugData\Level = Level
    
    ProcedureReturn #True
  EndProcedure
  Procedure.a GetDebugLevel()
    
    ProcedureReturn DebugData\Level
  EndProcedure
  Procedure.s GetDebugLog(Level.a = 0, Flags.u = 0)
    Protected MsgData.AWLib_Debug_MessageData  
    Protected Msg.s
    
    AWLib_DebugLog_Internal_CheckFlags(@Flags)

    ForEach DebugData\MsgData()
      MsgData = DebugData\MsgData()
      If Flags & #OnlyDebugLevel
        If MsgData\Level = Level
          AWLib_DebugLog_Internal_CreateMsg(@MsgData, Flags)
          Msg + MsgData\Msg + Chr(10)
        EndIf
      ElseIf MsgData\Level >= Level
        AWLib_DebugLog_Internal_CreateMsg(@MsgData, Flags)
        Msg + MsgData\Msg + Chr(10)
      EndIf      
    Next
    
    ProcedureReturn Msg
  EndProcedure
  Procedure ShowLastLog(Flags.u = 0)
    Protected MsgData.AWLib_Debug_MessageData
    
    AWLib_DebugLog_Internal_CheckFlags(@Flags)

    MsgData = DebugData\MsgData()
    AWLib_DebugLog_Internal_CreateMsg(@MsgData, Flags)
    AWLib_DebugLog_Internal_ShowLog(MsgData\Msg, Flags)      
    
    ProcedureReturn #True
  EndProcedure
  Procedure SetDebugLogDateFormat(Format.s = "")
    
    If Format
      DebugData\DateFormat = Format
    Else
      DebugData\DateFormat = "[%dd.%mm.%yy]"
    EndIf
    
    ProcedureReturn #True
  EndProcedure
  Procedure SetDebugLogTimeFormat(Format.s = "")
    
    If Format
      DebugData\TimeFormat = Format
    Else
      DebugData\TimeFormat = "[%hh:%mm:%ss]"
    EndIf
    
    ProcedureReturn #True
  EndProcedure
  Procedure SetDebugLogFlags(Flags.u = 0)
    
    DebugData\StandardFlags = Flags
    
    ProcedureReturn #True
  EndProcedure
  Procedure.s GetDebugLogSorted(SortField.b, Order.b = #PB_Sort_Ascending, Level.a = 0, Flags.u = 0)
    Protected MsgData.AWLib_Debug_MessageData  
    Protected Msg.s, Offset, Type
    
    AWLib_DebugLog_Internal_CheckFlags(@Flags)
    
    Select SortField
      Case #DebugLog_Sort_Date
        Offset = OffsetOf(AWLib_Debug_MessageData\Date)
        Type = TypeOf(AWLib_Debug_MessageData\Date)
        
      Case #DebugLog_Sort_ID
        Offset = OffsetOf(AWLib_Debug_MessageData\ID)
        Type = TypeOf(AWLib_Debug_MessageData\ID)
        
      Case #DebugLog_Sort_Level
        Offset = OffsetOf(AWLib_Debug_MessageData\Level)
        Type = TypeOf(AWLib_Debug_MessageData\Level)
        
      Case #DebugLog_Sort_Msg
        Offset = OffsetOf(AWLib_Debug_MessageData\Msg)
        Type = TypeOf(AWLib_Debug_MessageData\Msg)  
    EndSelect
    
    NewList TempData.AWLib_Debug_MessageData()
    CopyList(DebugData\MsgData(), TempData())
    SortStructuredList(TempData(), Order, Offset, Type)
    
    ForEach TempData()
      MsgData = TempData()
      If Flags & #OnlyDebugLevel
        If MsgData\Level = Level
          AWLib_DebugLog_Internal_CreateMsg(@MsgData, Flags)
          Msg + MsgData\Msg + Chr(10)
        EndIf
      ElseIf MsgData\Level >= Level
        AWLib_DebugLog_Internal_CreateMsg(@MsgData, Flags)
        Msg + MsgData\Msg + Chr(10)
      EndIf      
    Next
    
    ProcedureReturn Msg
  EndProcedure
  Procedure.s GetLastLog(Flags.u = 0)
    Protected MsgData.AWLib_Debug_MessageData
    
    AWLib_DebugLog_Internal_CheckFlags(@Flags)

    MsgData = DebugData\MsgData()
    AWLib_DebugLog_Internal_CreateMsg(@MsgData, Flags)
    
    ProcedureReturn MsgData\Msg
  EndProcedure
  
EndModule

CompilerIf  #PB_Compiler_IsMainFile
  
  UseModule DebugLog
  
  ;wird benötigt um DebugDaten auf der Console auszugeben
  OpenConsole("DebugLog Example")
  
  ; Datum/Zeit/ID und Level standartmäßig anzeigen:
  SetDebugLogFlags(#ShowLogTime|#ShowMsgID|#ShowDebugLevel)
  
  ; ein paar Einträge erzeugen mit Level 0 bis 2
  For i = 1 To 20
    DebugLog(Chr(117 - i) +"-Test " + Str(i), i % 3)  ; wird nur mit Debug ausgeben
    Delay(200)  ; kurze Wartezeit für verschiedene Zeiten
  Next
  
  ; weitere Einträge ohne Debug aber mit Console ... außerdem wird nur die ID zusätzlich angezeigt
  For i = 21 To 25
    DebugLog("Mehr Tests: " + Str(i), 3, #ShowConsole|#ShowMsgID|#NoDebugOutput)
  Next
  
  ; Einträge sortiert Ausgeben ... NUR DebugLevel 2 ... nur ID und DebugLevel anzeigen
  PrintN("----- Sortiert nach ID und nur LVL 2 -----")
  PrintN(GetDebugLogSorted(#DebugLog_Sort_ID, #PB_Sort_Ascending, 2, #OnlyDebugLevel|#ShowMsgID|#ShowDebugLevel))
  
  ; weitere Beispiele:
  PrintN("----- DebugLevel 1 und höher anzeigen ... mit LvL und Zeit -----")
  PrintN(GetDebugLog(1, #ShowDebugLevel|#ShowLogTime))
  
  PrintN("----- ALLES anzeigen, sortiert nach Msg -----")
  PrintN(GetDebugLogSorted(#DebugLog_Sort_Msg, #PB_Sort_Ascending|#PB_Sort_NoCase, 0, #StandardFlags|#ShowLogDate))

  Debug "----- letzte Nachricht nochmal anzeigen nur Debug -----"
  ShowLastLog()
  
  PrintN("----- neues Format für Zeit/Datum) und letzte Nachricht anzeigen -----")
  SetDebugLogDateFormat("[Tag: %dd]")
  SetDebugLogTimeFormat("!Sek.%ss!Min.%ii!")
  PrintN(GetLastLog(#StandardFlags|#ShowLogDate))
  SetDebugLogDateFormat() ; Datum-Format zurücksetzen
  SetDebugLogTimeFormat() ; Zeit-Format zurücksetzen
  
  
  ;warten auf Eingabe und Log speichern / Programm beenden
  
  Define File.s
  PrintN("Log speichern? ... J/N")    
  File = Input()            ; einfache Abfrage
  If File 
    If UCase(File) = "N"
      End
    EndIf
  Else
    End
  EndIf
  
  PrintN("----- Log speichern -----")
  File = ""
  File = SaveFileRequester("Log speichern", "Log.txt", "Text| *.txt", 0)
  If File
    CreateFile(0, File)
    WriteString(0, GetDebugLog(0, #ShowDebugLevel|#ShowLogDate|#ShowLogTime|#ShowMsgID))
    CloseFile(0)
    PrintN("Datei " + File + " gespeichert")
  Else
    PrintN("Speichern abgebrochen")
  EndIf

CompilerEndIf
weil einfach einfach einfach ist ... mach' ich es anders