The gb.logging component implements a flexible system for logging Gambas applications. This component provides its functionality through the two classes Formatter and Logger.
The class Formatter (gb.logging) can be used like a function. This class formats a line to automatically replace the required logging data. You will find which format tags you can use in section 19.5.2.2.
In the Formatter class you can not - until Gambas 3.5.3 - use these four 4 format tags:
$(callLocation); $(callFile); $(callLine) und $(callFunction)
Example:
The following statement in the source code determines on the one hand the log text and on the other hand the output format as well as the log level assigned to the log text (→ section 19.5.2.1 Level constants and 19.5.2.2 (Level property)):
It produces the following output in the Gambas IDE at runtime:
[18:59:04.227] - Test-Mitteilung - WARNING
You can insert the above statement (*) with your special protocol texts at different places in the source code. You can also use the following statement from the 'Error Management' section with the same effect:
Debug "[" & Format(Time(), "hh:nn:ss.uu") & "]" & " - Test-Mitteilung" & " - WARNUNG"
which writes the following output to the console of the IDE, which also contains information about the file, the event and the line in the source code:
FMain.btnDebug_Click.11: [18:59:04.227] - Test-Mitteilung - WARNUNG
The Logger (gb.logging) class provides, among other things, basic mechanisms for sending log texts. You can create as many logger objects as you need, because the gb.logging component is excellent for managing multiple logs for different log levels, for example.
The Logger class has the following features introduced in the next section.
Constant | Numeric value | Description |
---|---|---|
Critical | 0 | The level constant defines a protocol text as a critical message. |
Error | 1 | The level constant defines a protocol text as an error message. |
Warning | 2 | The level constant defines a protocol text as a warning. |
Info | 3 | The level constant defines a protocol text as an information message. |
Debug | 4 | The level constant defines a protocol text as debug message. |
Table 19.5.2.1.1: Overview of the level constants of the class Logger
Property | DataType | Default | Description |
---|---|---|---|
Format | String | * | Sets or returns the format for the log texts (logger object). |
Level | Integer | 3 | Sets or returns the minimum log level for a logger object. The values of Level are in the interval from 0 to 4 → constants. |
Table 19.5.2.2.1: Properties of the class Logger
The default * format of the Logger class is:
[$(now)] [$(levelname)] [$(callLocation)] $(message)
and only needs to be redefined by you (→ Table 19.5.2.1.1) if you want to set a different log text format.
Notes on the format:
A format string describes via the tags $(..) which information at which position is included in the protocol text:
Note the notation of the above tags in the format string (→ case-sensitive) !For the level property, insert either the constants or their numeric values, giving preference to the constants.You can also insert your own text or individual characters in the format string.
The Logger class has only the Logger.isEnabledFor(..) method:
The function returns True if, for example, the active Logger object 'MyLog' has been enabled for the log level specified by 'iLevel'.
To demonstrate the function, you can use this source code to check how much and which log levels are active for 'MyLog':
MyLog.Level = Logger.Error If MyLog.isEnabledFor(Logger.Critical) Then Print "Critical-Level aktiv!" If MyLog.isEnabledFor(Logger.Error) Then Print "Error-Level aktiv!" If MyLog.isEnabledFor(Logger.Warning) Then Print "Warning-Level aktiv!" If MyLog.isEnabledFor(Logger.Info) Then Print "Info-Level aktiv!" ' Default: Logger.Info (=3) If MyLog.isEnabledFor(Logger.Debug) Then Print "Debug-Level aktiv"
The console display shows the following two levels because with 'MyLog.Level = Logger.Error' only the log levels ≤ Logger.Error are active:
Critical-Level aktiv! Error-Level aktiv!
You can create a logger object like this:
Public hLogger As Logger hLogger = Logger ( [ iMinLevel As Integer, sOutput As String ] )
Examples:
(a) Logger object with the default level (=Logger.Info) to display the log texts in the console of the IDE.
(b) Logger object with a self-defined level (0..4) for displaying the log texts in the console of the IDE.
(c) Logger object with a self-defined level (0..4) and storage of the log texts in a log file in a directory in which the user also has write access.
(a) hLogger As New Logger ' Logger-Level » Info (b) hLogger As New Logger(2) ' Logger-Level » Warning (c) hLogger As New Logger(Logger.Error, Lower(User.Home &/ Application.Name & ".log"))
For an object of the Logger class - for example named 'MyLog':
Sub MyLog ( sMessage As String [ , iLevel As Integer ] )
with the two parameters:
to generate a log text for the active logger object 'MyLog'.
With the help of the procedure MyLog(..) and the use of a Logger object with its constants, properties as well as the method isEnabledFor(..) with 'Function isEnabledFor ( iLevel As Integer ) As Boolean' a flexible log system for a Gambas programme can be realised.
Just as with the Formatter class, you can also use the Logger class to send log texts to the standard output (→ console) and display them there.
For the development and testing of Gambas programmes, it is quite advantageous to save the log texts in a log file (→ variant (c)).
Here is a source code excerpt according to variant ©:
[1] Public MyLog As Logger [2] Public FilePath As String = Lower(User.Home &/ Application.Name & ".log") [3] Public bAppendMode As Boolean = True ' True → Log-Datei fortschreiben [4] [5] Public Sub Form_Open() [6] FMain.Center [7] FMain.Resizable = False [8] … [9] ' Minimales Log-Level, Log-Dateipfad und Modus festlegen [10] If Exist(FilePath) Then [11] If bAppendMode = False Then [12] Try Kill FilePath [13] Wait [14] MyLog = New Logger(Logger.Error, FilePath) [15] Endif ' bAppendMode = False ? [16] Endif ' Exist(FilePath) ? [17] MyLog = New Logger(Logger.Error, FilePath) [18] [19] ' Log-Format festlegen [20] MyLog.Format = "$(message)" [21] ' Log-Text ausgeben [22] If bAppendMode = True And Exist(FilePath) Then MyLog("", MyLog.Level) ' Leerzeile einfügen [23] [24] MyLog.Format = "$(message)" [25] MyLog(Format(Now, "dddd - dd. mmmm yyyy"), MyLog.Level) [26] [27] MyLog.Format = "$(time)" & gb.Tab & "$(message)" & " mit Log-Level " & " ≤ " & " $(levelname)" [28] MyLog("LOG-NEUSTART", MyLog.Level) [29] [30] MyLog.Format = "$(message)" [31] MyLog("---------------------------------------------------", MyLog.Level) [32] [33] ' Log-Format für alle weiteren Protokoll-Texte festlegen [34] MyLog.Format = "$(time)" & gb.Tab & "[$(levelname)] [$(callLocation)]" & " → " & "$(message)" [35] [36] End
Now you can log further log texts in the log file at the appropriate, often critical points in the source text with modified instructions:
MyLog("Eingabefehler Z1", Logger.Warning) or MyLog("Eingabe-String = " & sInput, Logger.Error) or MyLog("Division durch Null!", Logger.Critical) or MyLog.Level = Logger.Info MyLog("Änderung Log-Level auf " & MyLog.Level, MyLog.Level)
Here you can see a log file extract in 'Append' mode with different log levels:
Samstag - 18. Januar 2014 15:21:50.803 LOG-NEUSTART mit Log-Level ≤ WARNING --------------------------------------------------- 15:21:54.12 [CRITICAL] [FMain.IsComplex.339] → Eingabe-String = 3-4i,, 15:21:55.228 [WARNING] [FMain.btnAddieren_Click.139] → Eingabefehler Z1 oder Z2 15:21:57.9 [ERROR] [FMain.btnConvert_Click.96] → Eingabefehler Z1 15:21:59.719 [CRITICAL] [FMain.IsComplex.339] → Eingabe-String = 3-4i,, Samstag - 18. Januar 2014 15:41:07.429 LOG-NEUSTART mit Log-Level ≤ ERROR --------------------------------------------------- 15:41:14.908 [ERROR] [FMain.btnConvert_Click.96] → Eingabefehler Z1 15:41:16.282 [CRITICAL] [FMain.IsComplex.339] → Eingabe-String = 3-4i, 15:41:17.684 [ERROR] [FMain.btnIsComplex_Click.122] → Eingabefehler Z1
Notes:
You may want to limit the size of the log file if you want to continuously add to the log. An acceptable procedure is to delete the oldest log entry when a new one is added after a certain file size. Or, after a certain file size, you create an archive from the current log file and create a new log file.
The Excursus section introduces you to two log variants, both of which have their own special aspects.
The following source code implements the specifications of variant 1 in a module 'MyLog' - but without storing it in a log file:
' Gambas module file - Autor: Caveat - gambas@caveat.demon.co.uk PRIVATE iDebugMode AS Integer PRIVATE iLogIndex AS Integer PRIVATE cLogLines AS Collection PUBLIC CONST NO_DEBUG AS Integer = 0 PUBLIC CONST DEBUG_LOG_ONLY AS Integer = 1 PUBLIC CONST DEBUG_LOG_AND_PRINT AS Integer = 2 PUBLIC CONST DEBUG_MESSAGE_LOG_AND_PRINT AS Integer = 3 Public Sub SetDebugMode(iNewMode As Integer) iDebugMode = iNewMode End Public Sub LogMessage(sMessage As String, bShowAsError As Boolean) If iDebugMode = NO_DEBUG Then Return If cLogLines = Null Then cLogLines = New Collection Endif '-- cLogLines = Null cLogLines.Add(sMessage, Str(iLogIndex)) Inc iLogIndex If iDebugMode = DEBUG_LOG_AND_PRINT Then Print sMessage Else If iDebugMode = DEBUG_MESSAGE_LOG_AND_PRINT Then Message(sMessage) Print sMessage Endif ' iDebugMode = DEBUG_LOG_AND_PRINT If bShowAsError Then Message.Error(sMessage)' Zusätzliche Anzeige – aber nur bei Fehlern! End Public Sub DisplayLogLines() Dim sLogLine As String If cLogLines = Null Then Return For Each sLogLine In cLogLines Print cLogLines.Key & ". " & sLogLine Next End
You determine whether you want to see the log text only in the console or also as text in a separate message window with the procedure SetDebugMode(..) using the defined constants.
In the main programme, you call up protocol texts like this, for example:
MyLogger.SetDebugMode(1) ' DEBUG_LOG_ONLY ... Dim hFile As File Dim FilePath As String FilePath = "/home/hans/Bilder" &/ "gambas.png" Try hFile = Open FilePath For Read If Error Then MyLogger.LogMessage("Datei-Pfad: " & FilePath, True) MyLogger.LogMessage("Fehler: " & Error.Text & " @ " & Error.Where, False) Endif
The output of the log content via DisplayLogLines() shows for the above source text excerpt:
1. Datei-Pfad: /home/hans/Bilder/gambas.png 2. Fehler: Unable to load image @ Stock.LoadIcon.444
In variant 2, selected programme outputs - in this case temperature values - are read out at fixed time intervals and logged in a log file. This is done by the specified timer procedure MyTimer_Timer() from MyTimer. Each time the programme is restarted, you can decide whether the existing log file should be deleted or the log should be updated. Alternatively, you can also use a checkbox to specify this decision in the start procedure.
The source code is clear and is given in relevant excerpts:
Private MyTimer As Timer Public Sub Form_Open() ... MyTimer = New Timer As "MyTimer" MyTimer.Delay = 1000 * 120 ' Intervall der Datenspeicherung (→ 2 Minuten) ... End '-- Einbau in eine Start-Prozedur: ... If Exist(Application.Path &/ "rs232log.txt") Then If Message.Question("Letztes Mess-Protokoll löschen?", "Ja - löschen!", "Nein!") = 1 Then Try Kill Application.Path &/ "rs232log.txt" Wait Endif Endif AddTextToFile("MESS-PROTOKOLL") AddTextToFile("DATUM: " & Format(Now, "dd. mmmm yyyy")) AddTextToFile("---------------------------") MyTimer.Start ... Public Sub MyTimer_Timer() If RS232.Status = Net.Active Then AddTextToFile("> " & Format(Now, "hh:nn:ss") & " | " & "T = " & Asc(sTemperatureValue) & " °C") Endif ' RS232.Status = Net.Active ? End Public Sub AddTextToFile(Text As String) Dim hFile As File Dim FilePath As String FilePath = Application.Path &/ "rs232log.txt" Try hFile = Open FilePath For Append If Error Then Message.Error("Datei-Fehler") Return Endif Print #hFile, Text Close #hFile End
Here is an extract from a log file:
MESS-PROTOKOLL DATUM: 17. Januar 2014 --------------------------- > 09:47:56 | T = 22 °C > 09:49:56 | T = 24 °C > 09:51:56 | T = 26 °C > 09:53:56 | T = 28 °C
Of course, you can also log RS232 interface parameters or error messages in the log file.
Projects