{ Synopse mORMot framework Sample 11 - Exception logging purpose of this sample is to show basic logging mechanism of the framework TO HAVE LINE NUMBERS IN THE LOG FILE: - Go to Project/Options then set the Linker/File map setting to "Detailed" Version 1.0 - April 14, 2011 - Initial Release Version 1.18 - Kylix support } program LoggingTest; {$AppType console} {$I Synopse.inc} // all expected conditionals {$ifndef DELPHI5OROLDER} // mORMot.pas doesn't compile under Delphi 5 {$define WITHMORMOT} {$endif} uses {$I SynDprUses.inc} // use FastMM4 on older Delphi, or set FPC threads {$ifdef MSWINDOWS} Windows, ComObj, {$endif} SysUtils, {$ifdef WITHMORMOT} mORMot, {$endif} SynCommons, SynLog; type /// a class just to show how methods are handled TTestLogClass = class protected procedure TestLog; end; /// a custom exception used to show how Delphi exception are handled and // can be ignored on request ECustomException = class(Exception); {$ifdef WITHMORMOT} TSQLRecordPeople = class(TSQLRecord) private fFirstName: RawUTF8; fLastName: RawUTF8; fYearOfBirth: integer; fYearOfDeath: word; published property FirstName: RawUTF8 read fFirstName write fFirstName; property LastName: RawUTF8 read fLastName write fLastName; property YearOfBirth: integer read fYearOfBirth write fYearOfBirth; property YearOfDeath: word read fYearOfDeath write fYearOfDeath; end; {$else} // mORMot.pas doesn't compile under Delphi 5 (yet) TSQLLog = TSynLog; {$endif} var TestLevel: TSynLogInfo = high(TSynLogInfo); procedure TTestLogClass.TestLog; var ILog: ISynLog; S: TSynLogInfos; begin ILog := TSQLLog.Enter(self); // do some stuff ILog.Log(sllCustom1); ILog.Log(sllInfo,'TestLevel',TypeInfo(TSynLogInfo),TestLevel,nil); ILog.Log(sllInfo,'set',TypeInfo(TSynLogInfos),S,nil); ILog.Log(sllDebug,ILog.Instance); ILog.Log(sllExceptionOS, 'Some error with stacktrace from %', [ExeVersion.ProgramName], self); if TestLevel=low(TestLevel) then TTestLogClass(nil).ClassName; // will raise an access violation dec(TestLevel); TestLog; end; procedure TestLogProc; var ILog: ISynLog; begin ILog := TSQLLog.Enter; ILog.Log(sllDebug,'GarbageCollector',GarbageCollector); ILog.Log(sllDebug,GarbageCollector); end; procedure TestsLog; {$ifdef WITHMORMOT} procedure TestPeopleProc; var People: TSQLRecordPeople; Log: ISynLog; begin Log := TSQLLog.Enter; People := TSQLRecordPeople.Create; try People.IDValue := 16; People.FirstName := 'Louis'; People.LastName := 'Croivebaton'; People.YearOfBirth := 1754; People.YearOfDeath := 1793; Log.Log(sllInfo,People); finally People.Free; end; end; {$endif} procedure Proc2(n1, n2: Integer); forward; // test nested procedure Proc1(n1, n2: Integer); begin if n1 = 0 then try TTestLogClass(nil).ClassName; // will raise an access violation except on E: Exception do TSQLLog.Add.Log(sllInfo,'^^^^^^^^ recursion test Proc1',e); end else Proc2(n1 - 1, n2); end; procedure Proc2(n1, n2: Integer); begin if n2 = 0 then try TTestLogClass(nil).ClassName; // will raise an access violation except on E: Exception do TSQLLog.Add.Log(sllInfo,'^^^^^^^^ recursion test Proc2',e); end else Proc1(n1, n2 - 1); end; var i: integer; f: system.TextFile; info: TSynLogExceptionInfoDynArray; begin i := 1; // we need this to circumvent the FPC compiler :) // first, set the TSQLLog family parameters with TSQLLog.Family do begin Level := LOG_VERBOSE; //Level := [sllException,sllExceptionOS]; //PerThreadLog := true; //HighResolutionTimeStamp := true; //AutoFlushTimeOut := 5; OnArchive := EventArchiveSynLZ; //OnArchive := EventArchiveZip; ArchiveAfterDays := 1; // archive after one day end; TSQLLog.Add.Log(sllInfo,'Starting'); writeln(' try some low-level common exceptions'); try dec(i); if 10 div i=0 then; // will raise EDivByZero except on E: exception do TSQLLog.Add.Log(sllStackTrace,'^^^^^^^^ the first sample, divide by 0',E); end; try closefile(f); readln(f); // will raise EIOError (no console is available to read from) except on E: exception do TSQLLog.Add.Log(sllStackTrace,'^^^^^^^^ the next sample, I/O error',E); end; writeln(' try EAccessViolation in nested procedure calls (see stack trace)'); Proc1(5,7); Proc2(7,5); writeln(' try a method recursive call, with an EAccessViolation raised within'); with TTestLogClass.Create do try try TestLog; except on Exception do; // just ignore now end; finally Free; end; writeln(' try a procedure call with Enter/Auto-Leave'); TestLogProc; {$ifdef WITHMORMOT} writeln(' try a procedure call with Enter/Auto-Leave and a TSQLRecordPeople logging'); TestPeopleProc; {$endif} writeln(' try a custom Delphi exception'); try raise ECustomException.Create('Test exception'); // logged to TSQLLog except on E: Exception do TSQLLog.Add.Log(sllInfo,'^^^^^^^^ custom exception type',E); end; writeln(' try a custom Delphi exception after been marked as to be ignored'); TSQLLog.Family.ExceptionIgnore.Add(ECustomException); try raise ECustomException.Create('Test exception'); except on E: Exception do TSQLLog.Add.Log(sllInfo,'^^^^^^^^ nothing should be logged just above',E); end; writeln(' try an Exception with message='' - see ticket [388c2768b6]'); try raise Exception.Create(''); except on E: Exception do TSQLLog.Add.Log(sllInfo,'^^^^^^^^ Exception.Message=""',E); end; writeln(' try an ESynException'); try raise ESynException.CreateUTF8('testing %.CreateUTF8',[ESynException]); except on E: ESynException do begin TSQLLog.Add.Log(sllInfo,'^^^^^^^^ ESynException',E); {$ifdef WITHMORMOT} TSQLLog.Add.Log(sllDebug,'ObjectToJSONDebug(E) = %',[ObjectToJSONDebug(E)],E); {$endif} TSQLLog.Add.Log(sllDebug,'FindLocation(E) = %',[TSynMapFile.FindLocation(E)],E); end; end; {$ifdef MSWINDOWS} writeln(' try a EOleSysError, as if it was triggered from the .Net CLR'); try raise EOleSysError.Create('Test',HRESULT($80004003),0); except on E: Exception do TSQLLog.Add.Log(sllInfo,'^^^^^^^^ should be recognized as NullReferenceException',E); end; {$endif} writeln('GetLastExceptions = '); GetLastExceptions(info); for i := 0 to high(info) do writeln(ToText(info[i])); end; begin TestsLog; writeln('------ finished'); end.