PureBasic Forum
http://forums.purebasic.com/english/

[Module] Logging with multiple/custom log targets.
http://forums.purebasic.com/english/viewtopic.php?f=12&t=72252
Page 1 of 1

Author:  Env [ Fri Feb 08, 2019 5:02 pm ]
Post subject:  [Module] Logging with multiple/custom log targets.

Hey folks,

Just sharing a module I put together for the purpose of having a neat and handy way to log messages.

Features:
    - Add/Remove Custom Log Handlers (e.g. If you wanted to create one for a text file output)
    - Methods protected with mutex, so should be fine using with threaded applications
    - Built-in console log output handler.
    - Enable/disable timestamp decoration
    - Set custom timestamp format
    - Verbose messages which will only be posted to the log output(s) when EnableVerbose is used.

Example Included - This runs on the console, so if you see nothing then compile as a Console target.

Code:
; ====================================================================================================
; Title:        Logging Module
; Description:  Module for logging messages to custom targets
; Author:       Michael R. King (Env)
; License:      MIT
; Revision:     1

; If you like it, feel free to use it, if you really like it then you can buy me coffee :)
; https://ko-fi.com/mikerking
; ====================================================================================================

DeclareModule Log
 
  Enumeration Level
    #Info
    #Warning
    #Error
    #Verbose
  EndEnumeration
 
  Prototype pLogOutput(Level, Message.s)        ;- Prototype for Log Outputs
 
  Declare AddLogOutput(*Output.pLogOutput)      ;- Add a logging output procedure
  Declare RemoveLogOutput(*Output.pLogOutput)   ;- Remove a logging output procedure
 
  Declare EnableVerbose(State = #True)          ;- Enable Verbose messages
  Declare EnableTimestamp(State = #True)        ;- Enable timestamp inclusion
  Declare SetTimestampFormat(Format.s = "%hh:%ii:%ss") ;- Set the Timestamp format
  Declare EnableConsoleOutput(State = #True)    ;- Enable built-in Console log output
 
  Declare Info(Message.s)                       ;- Post an informative message to the log
  Declare Warning(Message.s)                    ;- Post a warning to the log
  Declare Error(Message.s)                      ;- Post an error message to the log
  Declare Verbose(Message.s)                    ;- Post a verbose message to the log
 
EndDeclareModule

Module Log
 
  Structure sLogger
    *mutex
    verbose.a
    timestamp.a
    tsFormat.s
    List *output()
  EndStructure
 
  Global gLogger.sLogger
  With gLogger
    \mutex = CreateMutex()
    \timestamp = #True
    \tsFormat = "%hh:%ii:%ss"
  EndWith
 
  Procedure ConsoleLogOutput(Level, Message.s)
    Select Level
      Case Log::#Info
        ConsoleColor(7, 0)
      Case Log::#Warning
        ConsoleColor(14, 0)
      Case Log::#Error
        ConsoleColor(12, 0)
      Case Log::#Verbose
        ConsoleColor(3, 0)
    EndSelect
    PrintN(Message)
  EndProcedure
 
  Procedure AddLogOutput(*Output.pLogOutput)
    With gLogger
      RemoveLogOutput(*Output)
      LockMutex(\mutex)
      AddElement(\output())
      \output() = *Output
      UnlockMutex(\mutex)
    EndWith
  EndProcedure
 
  Procedure RemoveLogOutput(*Output.pLogOutput)
    With gLogger
      LockMutex(\mutex)
      ForEach \output()
        If \output() = *Output
          DeleteElement(\output())
          ProcedureReturn
        EndIf
      Next
      UnlockMutex(\mutex)
    EndWith
  EndProcedure
 
  Procedure EnableVerbose(State = #True)
    With gLogger
      If State
        \verbose = #True
      Else
        \verbose = #False
      EndIf
    EndWith
  EndProcedure
 
  Procedure EnableTimestamp(State = #True)
    With gLogger
      If State
        \timestamp = #True
      Else
        \timestamp = #False
      EndIf
    EndWith
  EndProcedure
 
  Procedure SetTimestampFormat(Format.s = "%hh:%ii:%ss")
    gLogger\tsFormat = Format
  EndProcedure
 
  Procedure EnableConsoleOutput(State = #True)
    If State
      AddLogOutput(@ConsoleLogOutput())
    Else
      RemoveLogOutput(@ConsoleLogOutput())
    EndIf
  EndProcedure
 
  Procedure.s Timestamp()
    If gLogger\timestamp
      ProcedureReturn "[" + FormatDate(gLogger\tsFormat, Date()) + "] "
    EndIf
    ProcedureReturn ""
  EndProcedure
 
  Procedure Info(Message.s)
    Protected *handler.pLogOutput
    With gLogger
      LockMutex(\mutex)
      ForEach \output()
        *handler = \output()
        *handler(#Info, Timestamp() + Message)
      Next
      UnlockMutex(\mutex)
    EndWith
  EndProcedure
 
  Procedure Warning(Message.s)
    Protected *handler.pLogOutput
    With gLogger
      LockMutex(\mutex)
      ForEach \output()
        *handler = \output()
        *handler(#Warning, Timestamp() + "Warning: " + Message)
      Next
      UnlockMutex(\mutex)
    EndWith
  EndProcedure
 
  Procedure Error(Message.s)
    Protected *handler.pLogOutput
    With gLogger
      LockMutex(\mutex)
      ForEach \output()
        *handler = \output()
        *handler(#Error, Timestamp() + "ERROR: " + Message)
      Next
      UnlockMutex(\mutex)
    EndWith
  EndProcedure
 
  Procedure Verbose(Message.s)
    Protected *handler.pLogOutput
    With gLogger
      LockMutex(\mutex)
      If \verbose
        ForEach \output()
          *handler = \output()
          *handler(#Verbose, Timestamp() + Message)
        Next
      EndIf
      UnlockMutex(\mutex)
    EndWith
  EndProcedure
 
EndModule

CompilerIf #PB_Compiler_IsMainFile
 
 ; ========= EXAMPLE =========
 
  ; Our Custom Log Output
  Procedure MyLogTarget(MessageLevel, Message.s)
    Debug Message
  EndProcedure
 
  ; Add our Custom Log Output to the Logger
  Log::AddLogOutput(@MyLogTarget())
 
  ; Use a Console to demonstrate the logging
  OpenConsole("Log Test")
  EnableGraphicalConsole(#True)
  Log::EnableConsoleOutput(#True)
 
  ; Enable Verbose messages
  Log::EnableVerbose(#True)
 
  ; Log some messages
  Log::Verbose("Example has Started (This is a verbose message which can be filtered out by disabling them)")
 
  Log::Info("This is just an informative message!")
  Log::Warning("This is a warning!")
  Log::Error("An error has happened.  Luckily this is just an example error.")
 
  Log::Info("The Example has ended. Hit return to quit")
  Input()
  End 0

CompilerEndIf

Author:  Env [ Fri Feb 08, 2019 5:12 pm ]
Post subject:  Re: [Module] Logging with multiple/custom log targets.

Example of a custom log file writer:

Code:
#Log_FileOutput = "log.txt"

Procedure FileLogOutput(MessageLevel, Message.s)
  Protected.i file
  If FileSize(#Log_FileOutput) < 1
    file = CreateFile(#PB_Any, #Log_FileOutput)
  Else
    file = OpenFile(#PB_Any, #Log_FileOutput)
  EndIf
  If IsFile(file)
    FileSeek(file, Lof(file))
    WriteStringN(file, Message)
    CloseFile(file)
  EndIf   
EndProcedure

Log::AddLogOutput(@FileLogOutput())

Author:  #NULL [ Sat Feb 09, 2019 9:51 am ]
Post subject:  Re: [Module] Logging with multiple/custom log targets.

Very nice, thanks :)

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/