Mega Code Archive

 
Categories / Delphi / OOP
 

Exception or Event Logger

Title: Exception or Event Logger Question: Each project has logical errors or by runtime. It would be fine to write these exceptions down to a file in order to find out, what's happened weeks ago ;) or to track component events Answer: Exceptions are mistakes and errors due to some run-time problem. This is obviously a wishy-washy definition, but generally run-time problems would be things like running out of memory whilst adding a data object or an index out of bounds. In our team we wrote a procedure (years ago), which intercepts those nasty things like exceptions by assigning a global new event-handler in the main-unit: {$IFDEF DEBUG} Application.OnException:= AppOnException; {$ENDIF} If DEBUG is not set the code runs at full speed, but I advise to set the handler all the time, cause then you can analyse each applications exception file. Normal testing should identify programming mistakes, whereas the other type of error are exceptions to the norm. The event-handler goes like this: procedure TCWForm.AppOnException(sender: TObject; E: Exception); var Addr: string[9]; FErrorLog: System.Text; FileNamePath: string; begin //writes errorlog.txt file FileNamePath:= extractFilePath(application.exeName) + 'errorlog.txt'; AssignFile(FErrorLog, FileNamePath); try System.Append(FErrorlog); except on EInOutError do Rewrite(FErrorLog); end; Addr:= IntToHex(Seg(ErrorAddr),4) + ';' +IntToHex(Ofs(ErrorAddr),4); Writeln(FErrorLog, format('%s[%s]%s%s',[DateTimeToStr(Now), getNetUserName, E.Message, Addr])); System.Close(FErrorLog);} MessageDlg('CW5' + E.Message +'. occured at: '+Addr,mtError,[mbOK],0); end; To avoid scope conflicts, Assign File replaces the Assign procedure that was available in previous versions of Delphi. The Addr also depends on the OS. Note, that you want still see exceptions on the screen, you get it with the last MessageDlg in the AppOnException-routine. Then you get an output in a well shaped manner: *************************ERRORLOG************************************ 26.09.99 12:09:16 [MAX] List index out of bounds 52FF;1226 26.09.99 13:05:28 [MAX] Database BezSpr not found 5F6F;1226 26.09.99 13:21:37 [THOMAS] List index out of bounds 69DF;1226 26.09.99 13:43:35 [MAX] GP fault in module CW5.EXE at 0002:3588 2A9F;1226 30.09.99 14:32:23 [SIMON] Cannot perform this operation on a closed dataset 320F;1254 30.09.99 14:35:36 [MAX] Record locked by another user. Table:GBK.DB Maybe, the function getNetUserName has to be changed, it depends on the operating-system or the database you deal with: function getNetUserName: string; var szVar: array[0..32] of char; begin DBIGetNetUserName(szVar); result:= StrPas(@szVar) ; end; Instead of Addr:= by an Exception, use in a 32-bit environment: mem: TMemoryStatus; mem.dwLength:=sizeOf(TMemoryStatus); GlobalMemoryStatus(mem); edit3.text:=intToStr((mem.dwAvailPageFile) div 1024) ; edit4.text:=intToStr((mem.dwAvailPhys) div 1024); Component Event Logger (Update 1.3.02) ------------------------------------------------------ On the other side you want to know component events to track down user or system behavior. This is also usefull to show the events on runtime in a listbox or to store it in a file. First your declare a procedure in your class: events: TListBox; procedure LogEvent(const EventStr: string; Component: TComponent = nil); Second you define the procedure with a listbox as events: procedure TransAct.LogEvent(const EventStr: string; Component: TComponent = nil); var ItemCount: Integer; begin if (csDestroying in ComponentState) or not Events.Visible then Exit; if (Component nil) and (Component.Name '') then Events.Items.Add(Format('%s(%s)', [EventStr, Component.Name])) else Events.Items.Add(EventStr); ItemCount := Events.Items.Count; Events.ItemIndex := ItemCount - 1; if ItemCount (Events.ClientHeight div Events.ItemHeight) then Events.TopIndex := ItemCount - 1; //tracing end; Third you call the procedure LogEvent in your code as you want, e.g.: LogEvent('OnDataChange', Sender as TComponent); LogEvent('BeforeOpen', DataSet); LogEvent('AfterClose', DataSet); procedure TransAct.DataSetBeforeClose(DataSet: TDataSet); begin LogEvent('BeforeClose'); end; procedure TransAct.DataSetError(DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction); begin LogEvent('OnDelete/OnEdit/OnPost Errors', DataSet); end; procedure TransAct.Disconnect(Connection: TADOConnection; var EventStatus: TEventStatus); begin LogEvent('Disconnect', Connection); end; Update 06.04.02 with automatic file writing each with a time-stamp ------------------------------------------------------------------- function TScanner.SaveLogData(const UserData: WideString; const CheckSum : DWORD) : Boolean; var SL : TStringList; FileName : String; begin SL := TStringList.Create; FileName := 'D:\Scanner\LogData\'+FormatDateTime('yyyymmdd-hhnnsszzz',Now)+'.txt'; SL.Text := UserData; SL.SaveToFile(FileName); SL.Free; Result := True; end; update 28.9.2009 ---------------------------------------------------------------- When you want to set the file from begin to end and save time and resources for each event: initialization assignFile(FErrorlog,'errorlog.txt'); append(FErrorlog) finalization closeFile(FErrorlog)