Page 1 of 1

[module] - Tiny module to create logfiles

Posted: Wed Jan 29, 2025 6:07 pm
by miso
Nothing advanced here, though might save a little time to someone. Small enough to be adjusted to specific needs. Save example to be able to run it.

Code: Select all

;-===MODULE DESCRIPTION===
;Tiny module to create plain text logfiles
;Created with PB 6.20 Beta 1
;Author:      :miso
;Creation date:2025.01.29
;Scope        :all platform, but tested only on Windows 7 64 bit
;Permissions  :777, do what you want, (author takes no responsibilities nor any claims)
;Known Issues :Does not check permissions for parent directory (platform compatibility reasons)

EnableExplicit
;-===MODULE DECLARATION===
DeclareModule log
  ;-===CONSTANTS===
  #DirectoryType = -2
  #BufferSize    = 2048
  ;-===GLOBALS===
  ;###
  ; these globals are public, but they're not meant to be changed manually outside from the module.
  ; They're here, because that way I can use them together with modulename inside the module,
  ; and that seems a bit more clear/distinctive to my eyes (personal preference)
  ;###
  Global directory.s, filename.s
  Global filehandle.i = #False
  Global opened.i = #False
  
  ;-===PROCEDURE DECLARATIONS===
  Declare.i setdirectory(dir.s)
  Declare.i setfile(filename.s)
  Declare.i open()
  Declare.i close()
  Declare.i delete()
  Declare.i write(entry.s)
  Declare.i write_plain(entry.s)
EndDeclareModule

;-===MODULE===
Module log
  ;-===AUXILIARY PROCEDURES===
  ;***************************************
  ;auxiliary function for date
  ;***************************************
  Procedure.s sub_getdate()
    ProcedureReturn FormatDate("%yyyy-%mm-%dd %hh:%mm:%ss :: ",Date())
  EndProcedure
  
  ;-===PUBLIC PROCEDURES===
  ;***************************************
  ;Sets the directory for the logfile
  ;***************************************
  Procedure.i setdirectory(dir.s)
    If log::opened
      Debug "error, trying to set directory, but a logfile is already opened"
      ProcedureReturn #False
    EndIf
    
    If FileSize(dir.s) = #DirectoryType
      log::directory.s = dir.s
      ProcedureReturn #True
    EndIf
    Debug "Error. Trying to set invalid log directory:" + dir.s
    ProcedureReturn #False
  EndProcedure
  
  ;***************************************
  ;Sets the name of the logfile
  ;***************************************
  Procedure.i setfile(name.s)
    If name.s<>""
      log::filename.s = name.s
      ProcedureReturn #True
    EndIf
    Debug "Error. Trying to set empty string as logfile name:" + name.s
    ProcedureReturn #False
  EndProcedure
  
  ;***************************************
  ;Opens a logfile to write
  ;***************************************
  Procedure.i open()
    If log::opened Or IsFile(log::filehandle.i)
      Debug "Error, logfile is already opened"
      ProcedureReturn #False
    EndIf
    log::filehandle.i = OpenFile(#PB_Any,log::directory.s+log::filename.s,#PB_File_Append)
    If Not IsFile(log::filehandle.i)
      Debug "Error, could not open the logfile "+log::directory.s+log::filename.s
      ProcedureReturn #False
    EndIf
    log::opened.i = #True
    FileBuffersSize(log::filehandle.i,log::#BufferSize)
    ProcedureReturn #True
  EndProcedure
  
  ;***************************************
  ;closes the logfile
  ;***************************************
  Procedure.i close()
    If Not log::opened Or Not IsFile((log::filehandle.i))
      Debug "Error, trying to close logfile that is not opened."
      ProcedureReturn #False
    EndIf
    CloseFile(log::filehandle.i)
    log::filehandle.i = #False
    log::opened.i = #False
    ProcedureReturn #True
  EndProcedure
  
  ;***************************************
  ;Deletes a logfile, closing it if opened
  ;***************************************
  Procedure.i delete()
    If log::opened : log::close() : EndIf
    If FileSize(log::directory.s+log::filename.s)
      DeleteFile(log::directory.s+log::filename.s)
      ProcedureReturn #True
    EndIf
    ProcedureReturn #False
  EndProcedure
    
  
  ;***************************************
  ;Writes an entry into the opened logfile
  ;***************************************
  Procedure.i write(entry.s)
    If Not log::opened Or Not IsFile(log::filehandle.i)
      Debug "Error, trying to write logentry, but logfile is not opened"
      ProcedureReturn #False
    EndIf
    WriteStringN(log::filehandle.i,sub_getdate()+entry.s)
    FlushFileBuffers(log::filehandle.i)
    ProcedureReturn #True
  EndProcedure
  
  ;*************************************************************
  ;Writes an entry into the opened logfile without date and time
  ;*************************************************************
  Procedure.i write_plain(entry.s)
    If Not log::opened Or Not IsFile(log::filehandle.i)
      Debug "Error, trying to write log entry, but logfile is not opened"
      ProcedureReturn #False
    EndIf
    WriteStringN(log::filehandle.i,entry.s)
    FlushFileBuffers(log::filehandle.i)
    ProcedureReturn #True
  EndProcedure
EndModule



;-===EXAMPLE USAGE===
;***************************************************************
;Example usage
;***************************************************************

log::setdirectory(GetCurrentDirectory())
log::setfile("mylog.log")
;log::delete() ;uncomment this to check and delete a possible previous logfile to make the log not continous
log::open()

log::write_plain("#######################################")
log::write("Started program")
log::write("Test log entry")
log::write("Program closed")
log::close()
;log::delete() ;uncomment this line to remove the created sample logfile from your disk.
End



Re: [module] - Tiny module to create logfiles

Posted: Wed Jan 29, 2025 7:48 pm
by Caronte3D
Nice! Thanks for sharing! :wink:

Re: [module] - Tiny module to create logfiles

Posted: Wed Jan 29, 2025 8:10 pm
by Quin
Adding this to my code archive for sure, thanks :8)

Re: [module] - Tiny module to create logfiles

Posted: Wed Jan 29, 2025 8:49 pm
by infratec
I think with a continious open log file it is not possible to use tail or similar programs.
I also miss a loglevel.

Re: [module] - Tiny module to create logfiles

Posted: Wed Jan 29, 2025 9:19 pm
by infratec
I use something like this:

Logging.pbi

Code: Select all

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf


Enumeration LogLevels
  #LogLevel_Emergency
  #LogLevel_Alert
  #LogLevel_Critical
  #LogLevel_Error
  #LogLevel_Warning
  #LogLevel_Notice
  #LogLevel_Informational
  #LogLevel_Debug
EndEnumeration


Global LogLevel.i   ; you can set it via program parameter at program start
LogLevel = #LogLevel_Warning

Global Dim LogLevel$(#LogLevel_Debug)
LogLevel$(#LogLevel_Emergency) = "[EMERGENCY] "
LogLevel$(#LogLevel_Alert) = "[ALERT] "
LogLevel$(#LogLevel_Critical) = "[CRITICAL] "
LogLevel$(#LogLevel_Error) = "[ERROR] "
LogLevel$(#LogLevel_Warning) = "[WARNING] "
LogLevel$(#LogLevel_Notice) = "[NOTICE] "
LogLevel$(#LogLevel_Informational) = "[INFORMATIONAL] "
LogLevel$(#LogLevel_Debug) = "[DEBUG] "

Global LogFilename$ ; you can set it to what ever you want at program start
CompilerIf #PB_Compiler_Debugger
  LogFilename$ = GetHomeDirectory() + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".log"
CompilerElse
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
      LogFilename$ = "/var/log/" + GetFilePart(ProgramFilename()) + ".log"
    CompilerCase #PB_OS_MacOS
      LogFilename$ = GetHomeDirectory() + "Library/Logs/" + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".log"
    CompilerCase #PB_OS_Windows
      LogFilename$ = GetPathPart(ProgramFilename()) + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".log"
  CompilerEndSelect
CompilerEndIf
  
CompilerIf #PB_Compiler_Thread
  Global LogMutex.i   ; needed to be thread safe
  LogMutex = CreateMutex()
CompilerEndIf


Procedure Logging(Text$, Level.i=#LogLevel_Warning)
  
  Protected File.i
  
  
  Debug FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss ", Date()) + LogLevel$(Level) + Text$
  If Level <= LogLevel
    
    CompilerIf #PB_Compiler_Thread
      LockMutex(LogMutex)
    CompilerEndIf
    
    File = OpenFile(#PB_Any, LogFilename$, #PB_File_Append)
    If File
      WriteStringN(File, FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss ", Date()) + LogLevel$(Level) + Text$)
      CloseFile(File)
    EndIf
    
    CompilerIf #PB_Compiler_Thread
      UnlockMutex(LogMutex)
    CompilerEndIf
    
  EndIf
  
EndProcedure



CompilerIf #PB_Compiler_IsMainFile
  
  ;LogLevel = #LogLevel_Debug
  ;LogFilename$ = "c:\tmp\logging.log"
  
  Logging("Started", #LogLevel_Notice)
  
  Logging("Warning test")
  
  Logging("Finished", #LogLevel_Notice)
  
CompilerEndIf

Re: [module] - Tiny module to create logfiles

Posted: Wed Jan 29, 2025 9:35 pm
by miso
@Infratec
I wanted something that has an output very similar to ogre log. The tail is just in the example, log level can be written together with log entry if required.
(might be changed to create html with red colored warnings) It's up to the developer how to use the module or what kind of warning level is needed. It's a barebone skeleton that can be adjusted.

(Your solution is nice and valid though, I'm not arguing)

Re: [module] - Tiny module to create logfiles

Posted: Wed Jan 29, 2025 9:43 pm
by infratec
And for real heavy logging multithreaded programs I wrote a logging pbi which places the entries in a list
which is written to disk in an own thread.

Else it was not possible to ensure the order of the log entries in a multithreaded program which uses an external lib with a logging callback.