B4J Question Non-UI - best way to write logs

AHilton

Active Member
Licensed User
Longtime User
One of the first things in my Main.AppStart ...

B4X:
    #If RELEASE
        ' Anything that would normally show in the console (log(), errors, etc.) will go to this file so that I can see what's going on later
        Utils.RedirectOutputToFile(File.DirApp & DirectorySeparator & "logs", "logs.txt")
    #End If


In my Utilities Module ...

B4X:
' Redirects Output to a file
' Usage: RedirectOutput(File.DirApp, "logs.txt")
Sub RedirectOutputToFile (Dir As String, FileName As String)
    #if RELEASE
        Dim out As OutputStream = File.OpenOutput(Dir, FileName, True) 'Set to True to append the logs
        Dim ps As JavaObject
        ps.InitializeNewInstance("java.io.PrintStream", Array(out, True, "utf8"))
        Dim jo As JavaObject
        jo.InitializeStatic("java.lang.System")
        jo.RunMethod("setOut", Array(ps))
        jo.RunMethod("setErr", Array(ps))
    #end if
End Sub
' Redirects Output back to the (standard) screen
Sub RedirectOutputToScreen (Dir As String, FileName As String)
    #if RELEASE
        Dim out As OutputStream = File.OpenOutput(Dir, FileName, False) 'Set to True to append the logs
        Dim ps As JavaObject
        ps.InitializeNewInstance("java.io.PrintStream", Array(out, True, "utf8"))
        Dim fd As JavaObject
        fd.InitializeStatic("java.io.FileDescriptor")
        Dim jout As JavaObject
        jout.InitializeNewInstance("java.io.FileOutputStream", Array(fd.GetField("out")))
        ps.InitializeNewInstance("java.io.PrintStream", Array(jout, True, "utf8"))

        Dim jo As JavaObject
        jo.InitializeStatic("java.lang.System")
        jo.RunMethod("setOut", Array(ps))
        jo.RunMethod("setErr", Array(ps))
    #end if
End Sub
 
Upvote 0

Alexander Stolte

Expert
Licensed User
Thank you.

Together with this:
B4X:
'Logging Level: Error
Public Sub Error(module As String,Message As String)
    Log(DateTime.Date(DateTime.Now) & " " & DateTime.Time(DateTime.Now) & " " & "ERROR" & " - [" & module & "] " & Message)
End Sub

'Logging Level: Info
Public Sub Info(module As String,Message As String)
    Log(DateTime.Date(DateTime.Now) & " " & DateTime.Time(DateTime.Now) & " " & "INFO" & " - [" & module & "] " & Message)
End Sub

'Logging Level: Warning
Public Sub Warning(module As String,Message As String)
    Log(DateTime.Date(DateTime.Now) & " " & DateTime.Time(DateTime.Now) & " " & "WARNING" & " - [" & module & "] " & Message)
End Sub
Usage:
cm_functions.Info("Main","API is running")
'07/30/2020 18:13:18 INFO - [Main] API is running
It's more powerful, this is like log4net in .NET
 
Upvote 0

AHilton

Active Member
Licensed User
Longtime User
Sure, I use a reporting class, too. It allows reporting levels somewhat like yours above; reporting to various places (standard log, database, files with or without what I posted above, email, sms, and a reporting server); etc.

The nice thing about what I posted above, is that it'll log everything ... whether you use the log() command or your cm_functions subs or not. The bad thing is that it logs *everything* whether you want it or not.
 
Upvote 0

tchart

Well-Known Member
Licensed User
Longtime User
I wrote a code module called Logger.

In my main I specify the log level in the AppStart

B4X:
    'FILE|ALL|DEBUG|INFO|WARN|OFF
    If log_level.ToUpperCase = "FILE"  Then Logger.Level = 99
    If log_level.ToUpperCase = "ALL"   Then Logger.Level = 9
    If log_level.ToUpperCase = "DEBUG" Then Logger.Level = 3
    If log_level.ToUpperCase = "INFO"  Then Logger.Level = 2
    If log_level.ToUpperCase = "WARN"  Then Logger.Level = 1
    If log_level.ToUpperCase = "OFF"   Then Logger.Level = 0
    
    #if DEBUG
        Logger.Level = 9
    #End If

Then instead of going Log() I just use it like this;

Logger.Info("Some information")
Logger.Error("An Error Occured")

B4X:
#IgnoreWarnings: 9,12

Sub Process_Globals
    Dim Level As Int
    'FILE|ALL|DEBUG|INFO|WARN|OFF
    '99  |9  |3    |2   |1   |0
End Sub

Sub Warn(Message As String)
    If Level >= 1 Then Log(GetTimeStamp & ":WARN:" & Message)
    If Level == 99 Then WriteTextLog(GetTimeStamp & ":WARN:" & Message)
End Sub

Sub Info(Message As String)
    If Level >= 2 Then Log(GetTimeStamp & ":INFO:" & Message)
    If Level == 99 Then WriteTextLog(GetTimeStamp & ":INFO:" & Message)
End Sub

Sub Debug(Message As String)
    If Level >= 3 Then Log(GetTimeStamp & ":DEBUG:" & Message)
    If Level == 99 Then WriteTextLog(GetTimeStamp & ":INFO:" & Message)
End Sub

Sub Error(Message As String)
    Log(GetTimeStamp & ":ERROR:" & Message)
    'If Level == 99 Then
    WriteTextLog(GetTimeStamp & ":ERROR:" & Message)
    'End If
End Sub

Sub WriteTextLog(Message As String)
    Dim Writer As TextWriter
    Writer.Initialize(File.OpenOutput(File.DirApp, "Logger.txt", True))
    Writer.WriteLine(GetTimeStamp & ":" & Message)
    Writer.Flush
    Writer.Close
End Sub

Sub DumpToFile(Message As String)
    File.WriteString(File.DirApp,GetTimeStamp.Replace(":","").Replace(" ","") & ".txt",Message)
End Sub

Sub GetTimeStamp As String
    '2018-09-28 06:47:27.723:INFO:oejs.Server:main: Started @1723ms'
    Dim df As String = DateTime.DateFormat
    Dim Output As String   
    DateTime.DateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
    Output = DateTime.date(DateTime.Now)   
    DateTime.DateFormat=df   
    Return Output   
End Sub
 
Upvote 0
Top