RandomClear icon

How to log all (handled) exceptions

RandomClear | PRO | 11/06/14 12:23:12 PM UTC | 0 ⭐ | 9201 👁️ | Never ⏰ | []
Delphi |

9.31 KB

|

None

|

0 👍

/

0 👎

// See also: http://pastebin.com/9P0PJcyc - "how to ignore particular exception"
 
// You may want to log catched exceptions from your "except" blocks.
// The best way to do it - is to insert a manual call to logging to each "except" block which you want to log.
// This approach is illustrated here: http://pastebin.com/ACtr0r6S or http://pastebin.com/XWpyDuw6
// Please, use this approach when possible.
 
// Sometimes you may want to log all exceptions in your application.
// For example, if you have too many already written code, 
// or if you are using 3rd party code which is not under your control.
// Then you can use "Catch handled exceptions" option to globally trap all exceptions, including handled exceptions.
 
 
//     -= Important Note =-
// Use "Catch handled exceptions" option with caution.
// Never enable this option for production WITHOUT altering processing for handled exceptions.
// Consider this code:
 
for File in Files do
  try
    // ... try to read "File" file ...
  except
    on E: EStreamError do // <- E is catched by your code, thus it is called "handled" exception
      CorruptedFiles.Add(File);
    // <- other exceptions classes are not handled by your code, thus it is called "unhandled" exceptions
  end;
if CorruptedFiles.Count > 0 then
  Application.MessageBox('The following files are corrupted: ' + CorruptedFiles.Text, 'Error.', MB_OK or MB_ICONSTOP);
 
// The above code EXPECTS exceptions coming from guarded code block.
// The code establish a recovery strategy for a certain class of exceptions (EStreamError).
// If you enable "Catch handled exceptions" option - such exceptions will be processes by EurekaLog.
// The code in question DOES NOT expecting that. 
// It certainly does not expect error dialogs, long delays (UI, sending, etc.).
// Therefore, you must either enable "Catch handled exceptions" option only for debugging purposes
// OR alter processing of handled exceptions.
 
// -= end of note =-
 
 
 
// You must perform the following 3 steps to log all handled exceptions.
 
// 1. Enable "Catch handled exceptions" option.
 
// 2. Use the following event handler:
 
procedure LogHandledExceptions(const ACustom: Pointer; AExceptionInfo: TEurekaExceptionInfo; var AHandle, ACallNextHandler: Boolean);
begin
  // If exception is handled - change processing logic
  if AExceptionInfo.Handled then
  begin
    LogException(AExceptionInfo, AHandled); // <- see below: option 3a or 3b
    Exit;
  end;
 
  // Unhandled exceptions remains unaltered
end;
 
initialization
  // Note that LogHandledExceptions will not be called for handled exceptions WITHOUT checked "Catch handled exceptions" option
  RegisterEventExceptionNotify(nil, LogHandledExceptions);
end.
 
// 3a. Use the following logging code:
 
procedure LogException(AExceptionInfo: TEurekaExceptionInfo; var AHandle: Boolean);
var
  LogBuilder: TBaseLogBuilder;
  LogFileName: String;
begin
  // Create a generator for log content.
  // You may use default class for your application (LogBuilderClass)
  // Or a specific class (TLogBuilder, TXMLLogBuilder or your own descendant)
  LogBuilder := LogBuilderClass.Create(AEI);  
  try
    // File name to save bug report to.
    // You may use AEI.BugIDStr, if you want to save each report into individual file.
    LogFileName := ExpandEnvVars(AEI.Options.OutputLogFile(''));
    // Generate and save bug report. File will be overwritten.
    LogBuilder.SaveToFile(LogFileName);
  finally
    FreeAndNil(LogBuilder);
  end;
 
  // Disable EurekaLog for this exception
  AHandle := False;
end;
 
// 3b. Or you can use the following logging code:
 
procedure LogException(AExceptionInfo: TEurekaExceptionInfo; var AHandle: Boolean);
begin
  // Set options for "silent" exception
  AExceptionInfo.Options.ExceptionDialogType     := edtNone;  // no dialog
  AExceptionInfo.Options.SenderClasses           := '';       // no sending
  AExceptionInfo.Options.AutoCrashOperation      := tbNone;   // no autocrash counting
  AExceptionInfo.Options.SaveLogFile             := True;     // save bug report to disk
  AExceptionInfo.Options.ExceptionsFilters.Clear;             // ignore exception filters
 
  // Log each exception to individual files
  if ExtractFileExt(AExceptionInfo.Options.OutputPath) <> '' then // if report path contains file name - delete it
    AExceptionInfo.Options.OutputPath := ExtractFilePath(AEI.Options.OutputPath);
  AExceptionInfo.Options.loAddBugIDInLogFileName := True;
  AExceptionInfo.Options.loAddComputerNameInLogFileName := False;
  AExceptionInfo.Options.loAddDateInLogFileName  := False;
  AExceptionInfo.Options.soExcCount              := True;
  AExceptionInfo.Options.ErrorsNumberToSave      := 1;
  AExceptionInfo.Options.loNoDuplicateErrors     := True;
  AExceptionInfo.Options.soAppVersionNumber      := True;     // <- this assumes that you are using version info; comment this otherwise
  AExceptionInfo.Options.loDeleteLogAtVersionChange := True;  // <- this assumes that you are using version info; comment this otherwise
  AExceptionInfo.Options.loSaveProcessesSection  := False;
 
  // Log only exception in current thread
  AExceptionInfo.Options.boPauseRTLThreads       := False;
  AExceptionInfo.Options.boPauseWindowsThreads   := False;
  AExceptionInfo.Options.csoShowWindowsThreads   := False;
  AExceptionInfo.Options.csoShowRTLThreads       := False;
  AExceptionInfo.Options.csoShowELThreads        := False;
 
  // Do not change AHandle - let EurekaLog to process exception
end;
 
 
// -= Differences between 3a and 3b =-
 
// 3a manually logs exceptions and fully disables EurekaLog for it.
// 3b reconfigures exception and let normal flow of EurekaLog code to process it according to altered options.
// Use 3a when you just need a simple .el file for exception.
// Use 3b when you need some of EurekaLog's features (such as counting duplicates).
 
// It is best to stick with option 3a when possible - to minimize overhead of processing handled exceptions.
// Remember, some code may use exceptions a lot 
// (for example, a certain well-known components set uses exceptions in OnDraw handler - 
// which means insane amount of exceptions during normal workflow of application;
// adding processing overhead for such exceptions may greatly slow down your application).
 
// Another "gotcha" when using logging of handled exceptions:
// You may log handled exceptions into individual bug report files,
// then attach these files to bug report for unhandled exception.
// This will deliver all handled exceptions bug reports to you.
// For example:
 
uses
  SyncObjs;
 
var
  HandledExceptionsFiles: TStringList;
  HandledExceptionsFilesCS: TCriticalSection; // <- protects access to global HandledExceptionsFiles in multi-threaded application 
 
procedure LogException(AExceptionInfo: TEurekaExceptionInfo; var AHandle: Boolean);
var
  LogFileName: String;
begin
  // ...
 
  // Construct file name to save .el bug report to
  LogFileName := ExtractFilePath(ExpandEnvVars(AEI.Options.OutputLogFile(''))) + 'Handled.el'; // <- just an example
 
  // Remember this file:
  HandledExceptionsFilesCS.Enter;
  try
    if HandledExceptionsFiles = nil then
    begin
      HandledExceptionsFiles := TStringList.Create;
      HandledExceptionsFiles.Sorted := True;
      HandledExceptionsFiles.Duplicates := dupIgnore;
    end;
    HandledExceptionsFiles.Add(LogFileName);
  finally
    HandledExceptionsFilesCS.Leave;
  end;
 
  // ...
  //  <- Use LogFileName to save bug report, e.g. LogBuilder.SaveToFile(LogFileName) or AEI.Options.OutputPath := LogFileName;
  // ...
end;
 
// Service routine: cleanups HandledExceptionsFiles with deleting all files
procedure DeleteHandledExceptionsFiles;
var
  X: Integer;
begin
  HandledExceptionsFilesCS.Enter;
  try
    if HandledExceptionsFiles = nil then
      Exit;
 
    for X := 0 to HandledExceptionsFiles.Count - 1 do
      DeleteFile(HandledExceptionsFiles[X]);
 
    FreeAndNil(HandledExceptionsFiles);
  finally
    HandledExceptionsFilesCS.Leave;
  end;
end;
 
procedure AttachHandledReports(const ACustom: Pointer; AExceptionInfo: TEurekaExceptionInfo;  const ATempFolder: String; AAttachedFiles: TStrings; var ACallNextHandler: Boolean);
var
  X: Integer;
  DestFileName: string;
begin
  HandledExceptionsFilesCS.Enter;
  try
    if HandledExceptionsFiles = nil then
      Exit;
 
    // Attach all files from HandledExceptionsFiles
    for X := 0 to HandledExceptionsFiles.Count - 1 do
    begin
      DestFileName := ATempFolder + ExtractFileName(HandledExceptionsFiles[X]);
      if CopyFile(PChar(HandledExceptionsFiles[X]), PChar(DestFileName), True) then
        AAttachedFiles.Add(DestFileName);
    end;
 
    // Files are attached and no longer needed - delete them
    DeleteHandledExceptionsFiles;
  finally
    HandledExceptionsFilesCS.Leave;
  end;
end;
 
initialization
  HandledExceptionsFilesCS := TCriticalSection.Create;
  // "Pack send files into single file (.elp)" option is checked?
  if CurrentEurekaLogOptions.sndPack then
    RegisterEventZippedFilesRequest(nil, AttachHandledReports)     // if yes - add reports inside .elp
  else
    RegisterEventAttachedFilesRequest(nil, AttachHandledReports);  // otherwise - add reports side-by-side with .el
finalization
  DeleteHandledExceptionsFiles;
  FreeAndNil(HandledExceptionsFilesCS);
end.

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎