THotLog

[ Home ]



Logging data

THotLog provides 2 methodes to log strings, and 2 more to log errors and exceptions.
The first one and the last two have overloaded definitions, resulting in a total of height procedure available to log anything.


1- Logging variables


Without parsing: AddStr( );

Procedure AddStr (aString: String);

The string aString wil be sent to the parser, which will optionnally add the line number if lineNumberring is set, and send the resulting string to the writer thread.
Except for the line number, the string will be written exactly like it was when sent.

Each string sent is outputed on a new line.


Parsing involved:

procedure Add(aString: String); overload;

The string aString wil be sent to the parser, which will attempt to parse it, before sending the resulting string or stringList to the writer thread.
(see later: parsing tags).

(Embeding tags is not an obligation: if no tag is met, the string is simply sent to the writer, like AddStr( );


Procedure Add (aStringList: TStringList); overload;

Like above, but works with a TStringList instance.
Each string will be parsed and the resulting stringList will be sent to the writer thread for output.

It provides a way to split preparation and logging, or to store complex definitions variables to be parsed.

THotLog variables "header" and "footer" are examples of such stringList usage:
Self.header := TstringList.Create;
  With Self.Header Do
  Begin
    Add('{/}{LNumOff}{*80*}');
    Add('>>>> Start {App_name}  v {App_ver}{80@}{&}{dte} {hms}{&}');
    Add('{@12}From : {App_path}');
    Add('{@12}Prms : {App_prm-}{/}');
  End;

  Self.footer := TStringList.Create;
  With self.Footer Do
  Begin
    Add('{LNumOff}');
    Add('<<<< Stop  {App_name}{80@}{&}{dte} {hms}{&}');
    Add('{*80*}{/}');
  End;
They are added to the log file that way:
hLog.Add (header); 
...
hLog.Add (footer);
and the result will look like this:
********************************************************************************
>>>> Start HotLogTest.exe  v 1.0.0.4                         2004-03-09 23:38:54
           From : C:\Program Files\Borland\Delphi6\Projects\HotLog\
           Prms : (No params)

...

<<<< Stop  HotLogTest.exe                                    2004-03-09 23:38:55
********************************************************************************


Procedure Add(style: TVarRecStyle; aConstArray: Array of Const); overload;

Like above again, but works with anarray of const.
This array can contain anything. The resulting stringlist will be sent to the writer thread for.

This is a way to output variables whithout regards to their type.
The first argument decides the output format of the elements in the second one.
It can be vsNone, vsBasic or, vsExtended.
The 2nd argument (aConstArray) is an array of const.
It's values will be formatted depending on the first one.

hLog.Add(style:TVarRecStyle; aConstArray:TConstArray) examples of use:


1: Using aStyle := vsNone;

var i: real;
begin
  Randomize;
  i := Random;
  hLog.Add(vsNone,['aString','{/}another one, followed by integers',1,2,3,'{/} and now, the "i" value :',i]);
end;
Result:
aString
another one, followed by integers123
and now, the "i" value :0,502231817925349

And all in one line:
hLog.Add(vsNone,['aString','another one, followed by integers',1,2,3,'and now, the "i" value :',i]);
will result in
aStringanother one, followed by integers123and now, the "i" value :0,502231817925349
The values are restituted like you sent them, without spaces or anything like that.
It's up to you to do the formating, when using aStyle=vsNone.

You'll find another example of vsNone usage in the crlf tag section.



2: Using aStyle := vsBasic;

The elements in the array will be separated by colons and surrounded by parenthesis. Taking again the above example but removing the formating tags:

hLog.Add(vsExtended,[['aString','{/}another one, followed by integers',1,2,3,'{/} and now, the "i" value :',i]);
will output:
(aString; another one, followed by integers; 1; 2; 3; and now, the "i" value :; 0,502231817925349);

3: Using aStyle := vsExtended;

Writes all available information to the log file:
hLog.Add(vsExtended,[['aString','{/}another one, followed by integers',1,2,3,'{/} and now, the "i" value :',i]);
will output:
AnsiString  :  aString
AnsiString  :  another one, followed by integers
Integer     :  1
Integer     :  2
Integer     :  3
AnsiString  :  and now, the "i" value :
Extended    :  0,502231817925349

An array can not be passed as an argument to the function.
You can pass arrays to hLog.Add( )
var  TWinVer: Array of string;
  ...
  TWinVer[0] := 'Unknown';
  TWinVer[1] := '16b';
  TWinVer[2] := '32b';
  hLog.Add(vsExtended,['This system could be ', TWinVer2Str]);
but the result is not as expected:
AnsiString  :  This system could be
Pointer     :  ^(0012F5B8)

Why does THotLog consider arrays to be generic pointers?

Well, basically, it comes from the fact that ConstArray transmitted to hLog may contain anything, including args received by your calling routine, or variables internal to that routine. But ('and yet'?) such values may not exist long enough: Memory is assigned as long as the routine works, and released as soon as your programm exits it.
Then, parts (or all) of the elements of such "arrays of const" may no longer exist just a few CPU cycles after your call to hLog.add() procedure. Especially when used to log exceptions. It is even highly possible that they do not exist anymore, when HLParser handles the message (it had maybe a lot of other messages to work with before this one, or was sleeping, and experiments a "hard wakeup".
Moreover, THotLog has to work as few as possible in the main thread, leaving the work to the parser, writer (and eventualy "feedback") threads. In order to meet this objective, and knowing that values into the array of Const received may be freed as soon as hLog.Add() returns, hLog copies everything it can, and passes the result to the parser (as a new TConstArray). But in the course of this copy, only the most basic elements are copied.

According to Rudy Velthuis (TeamB):
"VPointer and VObject don't have proper copy semantics so it is impossible to write generic code that copies the contents."

And it's not possible to make a copy of everything (imagine an array containing a whole db table ...). Then as long as VPointers and VObjects are concerned, hLog simply transmits to the parser their "vType" kind, and some basic information ("is the pointer Nil?", "what is the className of this object?", ...), nothing more.

Avoiding memory leaks:

The array of Const that you pass to hLog.Add(...) is your property. hLog will make a copy of it, and free this copy when no longer needed. But it doesn'tchange anything to YOUR array. This means that if it is declared somewhere else than in the calling function itself (thus being not freed when you'll leave it), you should free its contents, and set its length to 0 as soon as it is no longer needed, after hLog.Add() returns).


2- Logging exceptions and errors


Procedure AddException(ex: Exception; err: Integer=0; freeAfterPost: Boolean=False); overload;

This is the most basic way to log an exception raised during code execution:
ex is, not so surprisingly, the exception occured.
err is reserved for internal use.
If freeAfterPost is set to True, ex will be freed by the hLog.AddException procedure. Otherwise not.

This is an example:

Function DoSomethingAndDivide(x,y: Integer): Integer;
Begin
  Result := -1;  
  TRY
    ...
    Result := Abs(x div y);         // If y is worth zero, an EZeroDivide exception will appear.
  EXCEPT         
    On E:Exception Do
      hLog.AddException(E);         // nothing else to do.
      ...
  END;
End;
With an EZeroDivide exception and hLog.SetErrorCaption('*** E R R O R ***') the result will be like this:
***********************
***    E R R O R    ***   "Division par zéro"
*********************** 
To logg more informations, hLog provides an overloaded definition of the AddException procedure, with tha ability to handle much more things:


Procedure AddException(ex: Exception; func: String; args: Array of const; err: Integer=0; freeAfterPost: Boolean=False); overload;

This is the evolved way to logg an exception raising during code execution:
Two new parametres are defined: func is the name of the function in which the exception occurs.
args is an array of const that may handle anything you want.
The other parameters are the same than above.

This is an example:

Function DoSomethingAndDivide(Sender: TObject; x,y,z: Integer; aString: String): Integer;
Begin
  Result := -1;  
  TRY
    ...
    Result := Abs(x div (y*z));                                           // If y or z are worth zero, an EZeroDivide exception will appear.
  EXCEPT         
    On E:Exception Do
      hLog.AddException(E,'DoSomethingAndDivide',[Sender,x,y,z,aString]); // nothing else to do.
      ...
  END;
End;

Like above, hLog.SetErrorCaption( ) was called as hLog.SetErrorCaption('*** E R R O R ***').

The output will be:
********************************************************************************************
***    E R R O R    ***   "Division par zéro" in function :
***********************    DoSomethingAndDivide( TObject    : Button1 (TButton, "Compute");
                                                 Integer    : 1;
                                                 Integer    : 0; 
                                                 Integer    : -547; 
                                                 AnsiString : Some string pointed at by aString  )
*********************************************************************************************
If SetErrorViewStyle received vsBasic, the result would have been:
***    E R R O R    ***   "Division par zéro" in DoSomethingAndDivide(Button1,1,0,-547,Some string pointed at by aString);


Procedure AddError(err: Integer); overload;

This is the simbling function of the basic form of hLog.Add() above.
It is intended to handle errors.
It will output the same things than the functions above, and the error number (after the line prefetch, and before the error message).

err is the error number.


Procedure AddError(err: Integer; func: String; args : Array of const); overload;

This is the simbling function of the evolved form of hLog.Add() above.
It is intended to handle errors.
It will output the same things than the functions above, and the error number (after the line prefetch, and before the error message).

err is the error number. Other arguments unchanged.


Output examples:

hLog.AddError(err);
***********************
***    E R R O R    *** (15) "Le lecteur spécifié est introuvable"
***********************

hLog.AddError(err,'MyProc',[]);
********************************************************************************************
***    E R R O R    *** (15) "Le lecteur spécifié est introuvable" in MyProc
********************************************************************************************
15 is number passed as err.
The error description is the return value of SysErrorMessage(err).
The array of const can be empty.


Warning :
These two functions need to receive the error code. But reading it seems to reset it sometimes.
If you want to use it, you'll have to first copy it's value into an integer variable when reading, before sending it:

If Form1.OpenDialog1.Execute then
begin
  // Let's search an imaginary file, modifying the name of an existing one:
  AssignFile(F, Form1.OpenDialog1.FileName +'x');
  {$I-}
  Reset(F);
  // It seems that IOresult is reseted to 0 as soon as you read it,
  // We'll then store it before usage ...
  err := IOResult;          // IOResult is now worth 0: showMessage(IntToStr(IOresult)); -> "0"!
  If err <> 0 then
     hLog.AddError(err);
  {$I+}
End;